天天看點

了解使用static import 機制

J2SE 1.5裡引入了“Static Import”機制,借助這一機制,可以用略掉所在的類或接口名的方式,來使用靜态成員。本文介紹這一機制的使用方法,以及使用過程中的注意事項。

    在Java程式中,是不允許定義獨立的函數和常量(當然,準确的說,隻是被final修飾、隻能指派一次的變量)的。即使從它們本身的功能來看,完全不需要依附于什麼東西,也要找個類或接口作為挂靠機關才行(在類裡可以挂靠各種成員,而接口裡則隻能挂靠常量)。

    挂靠的方法,是把它們加上static修飾符,定義為這個類或接口的靜态成員。這方面的典型例子是java.lang.Math類——包含了大量的sin、cos這樣的“函數”和PI、E這樣的“常量”。

傳統上,在通路這些挂靠了的函數、變量和常量的時候,需要在前面加上它們挂靠機關的名稱。如果隻是偶爾通路這些東西一下,這樣的寫法可以工作得很好;但是如果要頻繁通路這些成員的話,這樣的寫法就顯得比較羅嗦了。

J2SE 1.5裡引入了“Static Import”機制,借助這一機制,可以用略掉所在的類或接口名的方式,來使用靜态成員。

“靜态導入”或者“靜态成員導入”

   Static Import機制常常被直譯成“靜态導入”。但是從含義上看,“靜态成員導入”是更為貼切的譯法。不過考慮到“靜态導入”這說法比較簡短,估計還是會有強大的生命力。

隻包括常量定義的接口

    有一種省去常量前的挂靠機關的變通做法:将所有的常量都定義到一個接口裡面,然後讓需要這些常量的類實作這個接口(這樣的接口有一個專門的名目,叫作“Constant Interface”)。

這個方法可以工作。但是,因為這樣一來,就可以從“一個類實作了哪個接口”推斷出“這個類需要使用哪些常量”,有“會暴露實作細節”的問題。

1.精确導入的方式

    精确的導入一個靜态成員的方法,是在源檔案的開頭部分(任何類或接口的定義之前),加上類似這樣的聲明:

import static 包名.類或接口名.靜态成員名;

    注意盡管這個機制的名目是叫做“Static Import”,但是在這裡的次序卻是正好相反的“import static”。一經導入之後,在整個源檔案的範圍内,就可以直接用這個成員的名字來通路它了。

清單1:用精确導入的方式,導入sin和PI

//精确的導入Math.sin和Math.PI
import static java.lang.Math.sin;
import static java.lang.Math.PI;

public class StaticImportSampleA {
    public static void main(String[] args) {
        System.out.println(sin(PI/2));//輸出“1.0”
    }
}      

遊離于包外的類和接口們的特别問題

Java語言并未要求每個類和接口都必須屬于某一個包。但是,在J2SE 1.4以後,無論是import語句也好,還是import static語句也好,都要求給一個所在包名出來。換而言之,對于不屬于任何包的類和接口,是既不能用import導入它本身,也不能用import static導入它的靜态成員的。

2.按需導入的方式

Static Import機制也支援一種不必逐一指出靜态成員名稱的導入方式。這時,要采用這樣的文法:

import static 包名.類或接口名.*;

注意這種方式隻是指出遇到來曆不明的成員時,可以到這個類或接口裡來查找,并不是把這個類或接口裡的所有靜态成員全部導入。

清單2:用按需導入的方式,導入sin和PI

//聲明遇到來曆不明的成員時到java.lang.Math中去尋找
import static java.lang.Math.*;

public class StaticImportSampleB {
    public static void main(String[] args) {
        System.out.println(sin(PI/2));//輸出“1.0”
    }
}      

3.可以導入的種種東西

使用import static語句,可以導入一個類裡的一切被static修飾的東西,包括變量、常量、方法和内類。

清單3:變量、常量、方法和内部類都可以導入

package com.example.p3;

public class StaticImportee {
    public static int one = 1;
    public static final int TWO = 2;
    public static int three() {
        return 3;
    }
    public static class Four {
        public int value() {
            return 4;
        }
    }
}package com.example.p3;
import static com.example.p3.StaticImportee.*;

public class StaticImporter {
    public static void main(String[] args) {
        System.out.println(one);
        System.out.println(TWO);
        System.out.println(three());
        System.out.println(new Four());
    }
}      

不受影響的通路控制

Static Import不能突破Java語言中原有的通路控制機制的限制,不過也并不在這方面增加新的限制。原來有權限通路的靜态成員,都可以被導入和使用;而原來無權限通路的靜态成員,用了這個方法之後也仍然是通路不能。

4.導入之間的沖突問題

不同的類(接口)可以包括名稱相同的靜态成員。是以,在進行Static Import的時候,可能會出現“兩個語句導入同名的靜态成員”的情況。

在這種時候,J2SE 1.5會這樣來加以處理:

  • 如果兩個語句都是精确導入的形式,或者都是按需導入的形式,那麼會造成編譯錯誤。
  • 如果一個語句采用精确導入的形式,一個采用按需導入的形式,那麼采用精确導入的形式的一個有效。

注意,如果兩個同名的靜态成員一個是屬性,而另一個是方法,那麼因為使用時的寫法有差異,不會造成任何的沖突。

清單4:采用精确導入的形式的一個有效

package com.example.p4;
import static com.example.p4.Importee1.name;
import static com.example.p4.Importee1.*;
import static com.example.p4.Importee2.*;
import static com.example.p4.Importee2.pass;

public class Importer {
    public static void main(String[] args) {
        System.out.println(name);//輸出“Name1”
        System.out.println(pass);//輸出“Pass2”
    }
}package com.example.p4;

public class Importee1 {
    public static String name = "Name1";
    public static String pass = "Pass1";
}package com.example.p4;

public class Importee2 {
    public static String name = "Name2";
    public static String pass = "Pass2";
}      

5.本地和外來的競争

有時候,導入的東西還可能和本地的東西相沖突,這種情況下的處理規則,是“本地優先”。

清單5:本地的優先于外來的

package com.example.p5;
import static com.example.Zero.*;

public class One {
    public static int flag = 1;
    public static void main(String[] args) {
        System.out.println(flag);//輸出“1”
    }
}package com.example.p5;

public class Zero {
    public static int flag = 0;
}      

6.Static Import的負面影響

在編譯期間,所有因Static Import的存在而簡化了的名字,都會被編譯器打回原型。是以在性能方面,Static Import沒有任何影響。但是名字簡化卻可能造成一些維護方面的問題。

去掉靜态成員前面的類型名,固然有助于在頻繁調用時顯得簡潔,但是同時也失去了關于“這個東西在哪裡定義”的提示資訊,增加了閱讀了解的麻煩。如果導入的來源很著名(比如java.lang.Math),或者來源的總數比較少,這個問題并不嚴重;但是在不屬于這兩種的情況下,這就不是基本可以忽略的問題了。

7.歸納總結

      借助J2SE 1.5裡提供的Static Import機制,可以用一種更簡單的方式,來通路類和接口的靜态成員。不過,使用這一機制并不是沒有代價的,在使用不當的時候可能給維護工作帶來一定的困擾。是以,在具體使用之前,還要作一些兩方面的權衡。