645
魔獸
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