Java 1.5 特性 : Static Import

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;
}

上述例子中可以看到,stringvector 兩種資料型別原本是 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));
  }
}

如果去除掉同名所帶來的後果的話,那麼靜態載入還是有一些好處。但是如果遇到同名方法和欄位的話,情況會變得相當複雜。所以我建議如果會遇到同名問題,那麼最好還是不要使用比較好些。

發佈留言

發佈留言必須填寫的電子郵件地址不會公開。 必填欄位標示為 *

5 − five =

返回頂端