Java靜態泛型方法
Java在JDK 5中引入了泛型,使用起來方便了很多,下面是一段很常見的泛型使用:
List<String> list = new ArrayList<String>();
泛型方法使用
不僅集合中可以使用,在定義類、介面和方法的時候也是經常使用的,但是關於泛型方法使用的場景還是不太多。下面從求兩個數的最大數的實現上來看一下泛型類和泛型方法的簡單使用:
泛型類(介面)
package me.codeboy.test; /** * generic test * Created by yuedong on 9/12/16. */ public class MathTest<T extends Comparable> { public static void main(String[] args) { MathTest<Integer> mathTest1 = new MathTest<Integer>(); MathTest<Double> mathTest2 = new MathTest<Double>(); System.out.println(mathTest1.max(1, 2)); System.out.println(mathTest2.max(2.0, 3.0)); } private T max(T t1, T t2) { return t1.compareTo(t2) > 0 ? t1 : t2; } }
泛型方法
/** * generic test * Created by yuedong on 9/12/16. */ public class MathTest { public static void main(String[] args) { MathTest mathTest = new MathTest(); System.out.println(mathTest.max(1, 2)); System.out.println(mathTest.max(2.0, 3.0)); } private <T extends Comparable> T max(T t1, T t2) { return t1.compareTo(t2) > 0 ? t1 : t2; } }
靜態泛型方法
/** * generic test * Created by yuedong on 9/12/16. */ public class MathTest { public static void main(String[] args) { System.out.println(max(1, 2)); System.out.println(max(2.0, 3.0)); } private static <T extends Comparable> T max(T t1, T t2) { return t1.compareTo(t2) > 0 ? t1 : t2; } }
泛型方法優缺點
優點很明顯,程式碼簡潔多了,或者可以說比普通的泛型泛型更為簡潔,網上有一段關於Android中頻繁使用 findViewById
方法的靜態泛型方法實現,被稱為見過最牛逼的Android程式碼,但是事物都有兩面性,靜態泛型方法也有相應的缺點,再看一段程式碼:
import java.util.ArrayList;
import java.util.List;
/**
* test entry
* Created by yuedong on 9/10/16.
*/
public class Test {
public static void main(String[] args) {
List<String> list = new ArrayList<>();
list.add("test");
//普通轉換
ArrayList<String> result1 = (ArrayList<String>) list;
//靜態泛型轉換
String result2 = convert(list);
}
private static <T> T convert(Object a) {
return (T) a;
}
}
上述程式碼是 編譯通過,執行異常,為什麼會出現這種現象呢?這是因為Java的泛型方法屬於偽泛型,在編譯的時候將進行型別擦除。普通的泛型方法在類構建時已經明確制定了對應的型別,而在靜態泛型方法中,型別是無法直接推測的,缺少了明確的型別,最終造成型別轉化異常。
Exception in thread "main" java.lang.ClassCastException: java.util.ArrayList cannot be cast to java.lang.String
原理探索
看到了上面的結果,不禁想了解下泛型方法在型別擦除後最終轉換成了什麼,反編譯上述靜態泛型方法編譯後的class檔案如下:
Compiled from "Test.java"
public class Test {
public Test();
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/util/ArrayList
3: dup
4: invokespecial #3 // Method java/util/ArrayList."<init>":()V
7: astore_1
8: aload_1
9: ldc #4 // String test
11: invokeinterface #5, 2 // InterfaceMethod java/util/List.add:(Ljava/lang/Object;)Z
16: pop
17: aload_1
18: checkcast #2 // class java/util/ArrayList
21: astore_2
22: aload_1
23: invokestatic #6 // Method convert:(Ljava/lang/Object;)Ljava/lang/Object;
26: checkcast #7 // class java/lang/String
29: astore_3
30: return
}
可以看到convert函式最終轉化後對應的位元組碼為 Method convert:(Ljava/lang/Object;)Ljava/lang/Object;
引數為Object型別,返回也為Object型別,而在接下來的 checkcast
操作中,由於 List
和 String
型別的不同,所以丟擲了異常。
private static <T extends List> T convert(Object a) {
return (T) a;
}
對於上述的程式碼反編譯後對應 Method convert:(Ljava/lang/Object;)Ljava/util/List;
中,可以看到此時引數為Object型別,返回為List型別。
小結
儘管Java中的泛型是偽泛型,但是泛型可以使程式碼更加的簡潔,只是在使用 普通泛型方法
和 靜態泛型方法
時需要特別注意型別轉化。
更多文章請訪問小胖軒。