天天看點

了解使用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”。一經導入之後,在整個源檔案的範圍内,就可以直接用這個成員的名字來通路它了。

//精确的導入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導入它的靜态成員的。

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

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

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

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

import static java.lang.Math.*;

public class StaticImportSampleB {

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

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;

        }

import static com.example.p3.StaticImportee.*;

public class StaticImporter {

        System.out.println(one);

        System.out.println(TWO);

        System.out.println(three());

        System.out.println(new Four());

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

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

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

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

如果一個語句采用精确導入的形式,一個采用按需導入的形式,那麼采用精确導入的形式的一個有效。

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

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 {

        System.out.println(name);//輸出“Name1”

        System.out.println(pass);//輸出“Pass2”

public class Importee1 {

    public static String name = "Name1";

    public static String pass = "Pass1";

public class Importee2 {

    public static String name = "Name2";

    public static String pass = "Pass2";

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

package com.example.p5;

import static com.example.Zero.*;

public class One {

    public static int flag = 1;

        System.out.println(flag);//輸出“1”

public class Zero {

    public static int flag = 0;

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

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

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

http://sunnylocus.iteye.com/blog/806185