逐漸挖掘Static Import,更簡(jiǎn)單的訪問(wèn)靜態(tài)成員 |
|
|
J2SE 1.5里引入了“Static Import”機(jī)制,借助這一機(jī)制,可以用略掉所在的類或接口名的方式,來(lái)使用靜態(tài)成員。本文介紹這一機(jī)制的使用方法,以及使用過(guò)程中的注意事項(xiàng)。 在Java程序中,是不允許定義獨(dú)立的函數(shù)和常量(當(dāng)然,準(zhǔn)確的說(shuō),只是被final修飾、只能賦值一次的變量)的。即使從它們本身的功能來(lái)看,完全不需 要依附于什么東西,也要找個(gè)類或接口作為掛靠單位才行(在類里可以掛靠各種成員,而接口里則只能掛靠常量)。掛靠的方法,是把它們加上static修飾 符,定義為這個(gè)類或接口的靜態(tài)成員。這方面的典型例子是java.lang.Math類——包含了大量的sin、cos這樣的“函數(shù)”和PI、E這樣的 “常量”。 傳統(tǒng)上,在訪問(wèn)這些掛靠了的函數(shù)、變量和常量的時(shí)候,需要在前面加上它們掛靠單位的名稱。如果只是偶爾訪問(wèn)這些東西一下,這樣的寫(xiě)法可以工作得很好;但是如果要頻繁訪問(wèn)這些成員的話,這樣的寫(xiě)法就顯得比較羅嗦了。
|
J2SE1.5里引入了“StaticImport”機(jī)制,借助這一機(jī)制,可以用略掉所在的類或接口名的方式,來(lái)使用靜態(tài)成員。“靜態(tài)導(dǎo)入”或者“靜態(tài)成員導(dǎo)入”StaticImport機(jī)制常常被直譯成“靜態(tài)導(dǎo)入”。但是從含義上看,“靜態(tài)成員導(dǎo)入”是更為貼切的譯法。不過(guò)考慮到“靜態(tài)導(dǎo)入”這說(shuō)法比較簡(jiǎn)短,估計(jì)還是會(huì)有強(qiáng)大的生命力。
只包括常量定義的接口有一種省去常量前的掛靠單位的變通做法:將所有的常量都定義到一個(gè)接口里面,然后讓需要這些常量的類實(shí)現(xiàn)這個(gè)接口(這樣的接口有一個(gè)專門的名目,叫作“ConstantInterface”)。這個(gè)方法可以工作。但是,因?yàn)檫@樣一來(lái),就可以從“一個(gè)類實(shí)現(xiàn)了哪個(gè)接口”推斷出“這個(gè)類需要使用哪些常量”,有“會(huì)暴露實(shí)現(xiàn)細(xì)節(jié)”的問(wèn)題。
1.精確導(dǎo)入的方式
精確的導(dǎo)入一個(gè)靜態(tài)成員的方法,是在源文件的開(kāi)頭部分(任何類或接口的定義之前),加上類似這樣的聲明:
import static 包名.類或接口名.靜態(tài)成員名;
注意盡管這個(gè)機(jī)制的名目是叫做“Static Import”,但是在這里的次序卻是正好相反的“import static”。
一經(jīng)導(dǎo)入之后,在整個(gè)源文件的范圍內(nèi),就可以直接用這個(gè)成員的名字來(lái)訪問(wèn)它了。
清單1:用精確導(dǎo)入的方式,導(dǎo)入sin和PI
//精確的導(dǎo)入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”
}
}
游離于包外的類和接口們的特別問(wèn)題:
Java語(yǔ)言并未要求每個(gè)類和接口都必須屬于某一個(gè)包。但是,在J2SE 1.4以后,無(wú)論是import語(yǔ)句也好,還是importstatic語(yǔ)句也好,都要求給一個(gè)所在包名出來(lái)。換而言之,對(duì)于不屬于任何包的類和接口,是既不能用import導(dǎo)入它本身,也不能用importstatic導(dǎo)入它的靜態(tài)成員的。
2.按需導(dǎo)入的方式
Static Import機(jī)制也支持一種不必逐一指出靜態(tài)成員名稱的導(dǎo)入方式。這時(shí),要采用這樣的語(yǔ)法:
import static 包名.類或接口名.*;
注意這種方式只是指出遇到來(lái)歷不明的成員時(shí),可以到這個(gè)類或接口里來(lái)查找,并不是把這個(gè)類或接口里的所有靜態(tài)成員全部導(dǎo)入。
清單2:用按需導(dǎo)入的方式,導(dǎo)入sin和PI
//聲明遇到來(lái)歷不明的成員時(shí)到j(luò)ava.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.可以導(dǎo)入的種種東西
使用import static語(yǔ)句,可以導(dǎo)入一個(gè)類里的一切被static修飾的東西,包括變量、常量、方法和內(nèi)類。
清單3:變量、常量、方法和內(nèi)類都可以導(dǎo)入
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());
}
}
不受影響的訪問(wèn)控制:
StaticImport不能突破Java語(yǔ)言中原有的訪問(wèn)控制機(jī)制的限制,不過(guò)也并不在這方面增加新的約束。原來(lái)有權(quán)限訪問(wèn)的靜態(tài)成員,都可以被導(dǎo)入和使用;而原來(lái)無(wú)權(quán)限訪問(wèn)的靜態(tài)成員,用了這個(gè)方法之后也仍然是訪問(wèn)不能。
4.導(dǎo)入之間的沖突問(wèn)題
不同的類(接口)可以包括名稱相同的靜態(tài)成員。因此,在進(jìn)行Static Import的時(shí)候,可能會(huì)出現(xiàn)“兩個(gè)語(yǔ)句導(dǎo)入同名的靜態(tài)成員”的情況。
在這種時(shí)候,J2SE 1.5會(huì)這樣來(lái)加以處理:
如果兩個(gè)語(yǔ)句都是精確導(dǎo)入的形式,或者都是按需導(dǎo)入的形式,那么會(huì)造成編譯錯(cuò)誤。
如果一個(gè)語(yǔ)句采用精確導(dǎo)入的形式,一個(gè)采用按需導(dǎo)入的形式,那么采用精確導(dǎo)入的形式的一個(gè)有效。
注意,如果兩個(gè)同名的靜態(tài)成員一個(gè)是屬性,而另一個(gè)是方法,那么因?yàn)槭褂脮r(shí)的寫(xiě)法有差異,不會(huì)造成任何的沖突。
清單4:采用精確導(dǎo)入的形式的一個(gè)有效
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.本地和外來(lái)的競(jìng)爭(zhēng)
有時(shí)候,導(dǎo)入的東西還可能和本地的東西相沖突,這種情況下的處理規(guī)則,是“本地優(yōu)先”。
清單5:本地的優(yōu)先于外來(lái)的
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的負(fù)面影響
在編譯期間,所有因Static Import的存在而簡(jiǎn)化了的名字,都會(huì)被編譯器打回原型。因此在性能方面,Static Import沒(méi)有任何影響。但是名字簡(jiǎn)化卻可能造成一些維護(hù)方面的問(wèn)題。
去掉靜態(tài)成員前面的類型名,固然有助于在頻繁調(diào)用時(shí)顯得簡(jiǎn)潔,但是同時(shí)也失去了關(guān)于“這個(gè)東西在哪里定義”的提示信息,增加了閱讀理解的麻煩。如果導(dǎo)入的來(lái)源很著名(比如java.lang.Math),或者來(lái)源的總數(shù)比較少,這個(gè)問(wèn)題并不嚴(yán)重;但是在不屬于這兩種的情況下,這就不是基本可以忽略的問(wèn)題了。
7.歸納總結(jié)
借助J2SE 1.5里提供的StaticImport機(jī)制,可以用一種更簡(jiǎn)單的方式,來(lái)訪問(wèn)類和接口的靜態(tài)成員。不過(guò),使用這一機(jī)制并不是沒(méi)有代價(jià)的,在使用不當(dāng)?shù)臅r(shí)候可能給維護(hù)工作帶來(lái)一定的困擾。因此,在具體使用之前,還要作一些兩方面的權(quán)衡。
參考資源:
1、可以通過(guò)Sun的Java Technology頁(yè)面找到下載J2SE 1.5的SDK及其文檔的鏈接,目前最新的版本是J2SDK 1.5Beta 2。注意在使用這一版本的javac的時(shí)候,要加上“-source 1.5”作為參數(shù),才能編譯使用了J2SE1.5中新增語(yǔ)言特性的源代碼。
2、John Zukowski在《馴服 Tiger:Tiger 預(yù)覽版現(xiàn)已推出》一文中,介紹了如何開(kāi)始使用J2SDK1.5的基礎(chǔ)知識(shí)。不過(guò)因?yàn)檫@篇文章是依照J(rèn)2SDK 1.5Alpha版的狀況所寫(xiě),所以里面提到的一些細(xì)節(jié)(如下載地址和默認(rèn)安裝路徑)已經(jīng)發(fā)生了變化。
3、《JSR 201: Extending the Java Programming Language with Enumerations,Autoboxing, Enhanced for loops and Static Import》定義了很多J2SE1.5中的新語(yǔ)言特性,包括了“Static Import”。
4、Joshua Bloch在《Effective Java》一書(shū)的第四章《Classes and Interfaces》中,解釋了只包括常量的接口所存在的問(wèn)題。
5、Calvin Austin在《J2SE 1.5 in a Nutshell》一文中,對(duì)J2SE 1.5中的各種新特性,進(jìn)行了全面而概括的介紹。
聯(lián)系客服