閱讀645 返回首頁    go 魔獸


Java泛型編程與多態、重載的同與不同

泛型編程

泛型編程在某些語言中也稱之為模板編程,比如C++,所以在泛型編程中見到的那個T也就是Template的首字母。
來看一個泛型編程的簡單樣例。

public static void main(String[] args) {
    StringBuilder sb = new StringBuilder("Test template");
    System.out.println(foo(sb).toString());
}

public static <T> T foo(T t) {
    // bla bla bla
    return t;
}

泛型編程與多態

在麵向對象的編程中還有一個概念叫多態,利用這種概念,我們可以將泛型編程中的T替換成所有對象的基類Object,這在某種程度上同樣能夠達到泛型編程所達到的效果,如下方代碼所示。

public static void main(String[] args) {
    StringBuilder sb = new StringBuilder("Test template");
    System.out.println(foo(sb).toString());
    System.out.println(foo2(sb).toString());
}

public static <T> T foo(T t) {
    // bla bla bla
    return t;
}

public static Object foo2(Object o) {
    // bla bla bla
    return o;
}

那麼這兩種方法有什麼區別嗎?

我們來看看字節碼。

public class template.Template {
  public template.Template();
    Code:
       0: aload_0
       1: invokespecial #1                  // Method java/lang/Object."<init>":()V
       4: return

  public static void main(java.lang.String[]);
    Code:
       0: new           #2                  // class java/lang/StringBuilder
       3: dup
       4: ldc           #3                  // String Test template
       6: invokespecial #4                  // Method java/lang/StringBuilder."<init>":(Ljava/lang/String;)V
       9: astore_1
      10: getstatic     #5                  // Field java/lang/System.out:Ljava/io/PrintStream;
      13: aload_1
      14: invokestatic  #6                  // Method foo:(Ljava/lang/Object;)Ljava/lang/Object;
      17: checkcast     #2                  // class java/lang/StringBuilder
      20: invokevirtual #7                  // Method java/lang/StringBuilder.toString:()Ljava/lang/String;
      23: invokevirtual #8                  // Method java/io/PrintStream.println:(Ljava/lang/String;)V
      26: getstatic     #5                  // Field java/lang/System.out:Ljava/io/PrintStream;
      29: aload_1
      30: invokestatic  #9                  // Method foo2:(Ljava/lang/Object;)Ljava/lang/Object;
      33: invokevirtual #10                 // Method java/lang/Object.toString:()Ljava/lang/String;
      36: invokevirtual #8                  // Method java/io/PrintStream.println:(Ljava/lang/String;)V
      39: return

  public static <T> T foo(T);
    Code:
       0: aload_0
       1: areturn

  public static java.lang.Object foo2(java.lang.Object);
    Code:
       0: aload_0
       1: areturn
}

foo方法調用時的字節碼

14: invokestatic  #6                  // Method foo:(Ljava/lang/Object;)Ljava/lang/Object;
17: checkcast     #2                  // class java/lang/StringBuilder
20: invokevirtual #7                  // Method java/lang/StringBuilder.toString:()Ljava/lang/String;

foo2方法調用時的字節碼

30: invokestatic  #9                  // Method foo2:(Ljava/lang/Object;)Ljava/lang/Object;
33: invokevirtual #10                 // Method java/lang/Object.toString:()Ljava/lang/String;

不難發現,main函數調用使用了泛型編程的foo方法時,其字節碼已不再是T,而是替換為實際的StringBuilder類;反觀foo2方法使用的還是Object,那它將是在運行時作類型轉換。
結論:泛型編程是編譯期替換;多態則是運行期作類型轉換。顯而易見,泛型編程類型安全。

泛型編程與重載

什麼是重載

不廢話,形如下方代碼即重載。

public static int add(int a, int b) {
    return a + b; 
}

public static double add(double a, double b) {
    return a + b;
}

重載的特點:兩個及以上方法名相同;參數個數不同,參數順序不同、類型不同,以上任一種及以上都可以構成重載。僅返回值不同不可構成重載。

測試的完整代碼如下:

public static void main(String[] args) {
    StringBuilder sb = new StringBuilder("Test template");
    System.out.println(foo(sb).toString());
    System.out.println(foo2(sb).toString());
    int a = 1;
    int b = 2;
    System.out.println(add(a, b));

    double x = 1.1;
    double y = 1.2;
    System.out.println(add(x, y));
}

public static <T> T foo(T t) {
    // bla bla bla
    return t;
}

public static Object foo2(Object o) {
    // bla bla bla
    return o;
}

public static int add(int a, int b) {
    return a + b; 
}

public static double add(double a, double b) {
    return a + b;
}

接下來看看重載部分的字節碼:

48: invokestatic  #11                 // Method add:(II)I
71: invokestatic  #17                 // Method add:(DD)D

總結:看來字節碼已經做了相應的轉換,重載和泛型編程相似,都是在編譯期就做了替換,而不是在運行時,因此重載也是類型安全的,但是重載需要為每一種不同的參數類型重新編寫代碼,代碼複用度不高。

總結

泛型編程和重載是在編譯期作了類型替換,多態則是在運行期作類型替換。
泛型編程和重載是類型安全的,多態則有類型轉換風險。
泛型編程和多態代碼複用度高,重載代碼複用度低。

最後更新:2017-07-24 00:02:49

  上一篇:go  我的考駕照之路
  下一篇:go  前端資源