目錄
C++ 的 #include
有學習過 C 或 C++ 的人,在初學 Java 的時候,常常會將 Java 的 import
指令和 C++ 中的 #include
指令互相搞混,反倒是沒程式基礎的人來學習 Java 時不容易產生這樣的問題。這或許可以算是「從前的經驗造成學習上的誤解」的例子。
C++ 在連結編譯的時候,會依照 #include
所指定的標頭檔而將相關的檔案連結起來,最後成為一執行檔。而所有被 include 進來的 .h 檔案,可以直接使用其中所定義好的函式。如果我們 include 了一堆檔案,難免會有重複的函式名稱,所以後來 C++ 也加入了命名空間 namespace
的概念,以降低多重定義所帶來的影響:
#include <iostream>
#include <string>
#include <vector>
using namespace std;
int main(int argc, char **argv) {
vector<string> *vMp3Dirs = new vector<string>; // 要 #include <vector>
string sPath = ""; // 要 #include <string>
return 0;
}
上述例子中可以看到,string
和 vector
兩種資料型別原本是 C++ 所沒有提供的,但是在我們 include 相關標頭檔後,我們就可以「直接」在程式區塊中使用它們。
Java 中的 import
和 C++ 的 #include
是不一樣的。import
主要的意思是告訴編譯器,程式所需要的類別,在那一個命名空間底下。不像 C++ 的是,C++ 是會把相關的二進位程式碼加入我們的程式碼中,所以我們 include 的檔案愈多,那麼程式就可能愈肥大;Java 的做法是只單純的指定出命名空間 (實際上會對應到目錄),編譯時會檢查其指定的類別是否存在。等到執行時,再依照 import 所指定的命名空間去載入所需的類別。
Java 中的靜態載入 Static Import
我們在使用 Math 類別中的靜態方法時,需要撰寫 Math.min(a, b)
這樣的敘述句,而不像 C++ 中可以直接呼叫 min(a, b)
。當然這樣的好處是可以避免有許多同名方法時,會產生模擬兩可的情形發生。不過在新版中的靜態載入,允許我們直接使用靜態方法和欄位:
import static java.lang.Math.*;
public class StaticImport1 {
public static void main(String[] args) {
double d = 2.5, e = 3.0;
System.out.println(min(d, e)); // 不需要使用 Math.min(d, e)
System.out.println(PI * pow(d, e));
}
}
同名方法和欄位
既然原本 Java 的設計是我們必須要指定類別名稱,使得我們可以避免數個類別中的同名方法和欄位,現在加上一個靜態載入,那豈不是開倒車嗎?事實上我認為靜態載入雖然可以少寫一些程式碼,但這也造成了同名混淆引起的問題。所以基本上如果會造成同名混淆的話,那麼我建議不要使用靜態載入。不過如果真的必須要用靜態載入時,又遇到同名的情況,必須使用底下的方法解決:
import static java.lang.Math.*;
import static test.MyMath.*;
public class StaticImport2 {
public static void main(String[] args) {
double d = 2.5, e = 3.0;
// System.out.println(min(d, e)); // 會出現 reference to min is ambiguous
// System.out.println(MyMath.min(d, e)); // 會出現 cannot find symbol
System.out.println(test.MyMath.min(d, e)); //必須指定完整套件路徑
System.out.println(Math.min(d, e)); // 因為 Math 位在 java.lang 套件中
System.out.println(PI * pow(d, e));
}
}
// MyMath.java
package test;
public class MyMath {
public static double min(double a, double b) {
return Math.abs(a - b);
}
}
這讓我很懷疑的是,如果有函式或方法同名的話,我必須要指定「完整」的命名空間,才能正常呼叫到我想存取的方法。既然如此的話,直接 import 不是更快嗎?:
import static java.lang.Math.*;
import test.MyMath; // 不使用靜態載入
public class StaticImport2 {
public static double min(double a, double b) {
return Math.abs(a - b);
}
public static void main(String[] args) {
double d = 2.5, e = 3.0;
System.out.println(min(d, e)); // 會呼叫到自己的靜態函式
System.out.println(MyMath.min(d, e)); // 不需完整名稱
System.out.println(Math.min(d, e)); // 如果本類別中有同名靜態函式,還是得加上 Math
System.out.println(PI * pow(d, e));
}
}
如果去除掉同名所帶來的後果的話,那麼靜態載入還是有一些好處。但是如果遇到同名方法和欄位的話,情況會變得相當複雜。所以我建議如果會遇到同名問題,那麼最好還是不要使用比較好些。