Java筆記:泛型
一、簡單實例
class Solution<T> { private T obj; Solution(T obj) { this.obj = obj; } public T getObj() { return obj; } public static void main(String[] args) { Solution<Integer> solution = new Solution<>(100); Object obj = solution.getObj(); System.out.println(obj.getClass().getName());View Code//java.lang.Integer System.out.println(obj);//100 } }
二、類型安全
若簡單地使用Object來替代泛型並確保類型轉換正確,那麽即使不適用泛型也能得到相同的功能。但是如果類型轉換不正確,程序就會在運行時發生錯誤,使用泛型可以自動確保類型安全,這個過程中消除了手動輸入類型轉換以及類型檢查的需要,運行時的錯誤就可以轉換為編譯時的錯誤,這是泛型的主要優勢。
三、多個類型參數
class Solution<T, V> { private T objA; private V objB; Solution(T objA, V objB) {View Codethis.objA = objA; this.objB = objB; } public T getObjA() { return objA; } public V getObjB() { return objB; } public static void main(String[] args) { Solution<Character, Integer> solution = new Solution<>(‘0‘, 0); System.out.println(solution.getObjA()); System.out.println(solution.getObjB()); } }
四、有界類型
通過指定超類限制能夠傳遞給類型參數的類型。
class Solution<T extends Number> { private T[] arr; double getSum() { double sum = 0; for (T i : arr) sum += i.doubleValue(); return sum; } Solution(T... nums) { arr = nums; } public static void main(String[] args) { Solution<Integer> solution = new Solution<>(1, 2, 3, 4, 5); System.out.println(solution.getSum()); } }View Code
除了指定超類作為邊界之外,還可以指定接口作為邊界。
五、通配符參數
boolean sameSum(Solution<T> ob) { return getSum() == ob.getSum(); }View Code
假設我們需要比較具有不同泛型參數的相同類型對象,上述代碼並不能滿足要求。只有當比較對象具有相同的泛型參數時,上述代碼才可以被調用。正確的解決方法需要用到通配符,通配符同樣可指定為有界。
class Solution<T extends Number> { private T[] arr; double getSum() { double sum = 0; for (T i : arr) sum += i.doubleValue(); return sum; } boolean sameSum(Solution<? extends Number> ob) { return getSum() == ob.getSum(); } Solution(T... nums) { arr = nums; } public static void main(String[] args) { Solution<Integer> solutionA = new Solution<>(1, 2, 3, 4, 5); Solution<Double> solutionB = new Solution<>(5.0, 4.0, 3.0, 2.0, 1.0); System.out.println(solutionA.sameSum(solutionB));//true } }View Code
六、泛型方法
class Solution { static <T extends Number, V extends Number> boolean contain(T[] arr, V x) { for (int i = 0; i < arr.length; i++) if (arr[i].doubleValue() == x.doubleValue()) return true; return false; } public static void main(String[] args) { Double[] arr = {1.0, 2.0, 3.0, 4.0, 5.0}; System.out.println(contain(arr, 1)); } }View Code
構造函數同樣支持泛型。
class Solution { private double number; <T extends Number> Solution(T t) { number = t.doubleValue(); } }View Code
七、泛型接口
泛型接口與泛型類相似。
interface MinMax<T extends Comparable<T>> { T min(); T max(); } class Solution<T extends Comparable<T>> implements MinMax<T> { private T[] arr; @Override public T min() { T res = arr[0]; for (int i = 1; i < arr.length; i++) if (res.compareTo(arr[i]) > 0) res = arr[i]; return res; } @Override public T max() { T res = arr[0]; for (int i = 1; i < arr.length; i++) if (res.compareTo(arr[i]) < 0) res = arr[i]; return res; } Solution(T... nums) { arr = nums; } public static void main(String[] args) { Solution<Integer> ob = new Solution<>(1, 2, 3, 4, 5); System.out.println(ob.min()); System.out.println(ob.max()); } }View Code
八、歷史遺留
早期的Java是不支持泛型的,現在仍然存在大量歷史遺留代碼。為了使這些遺留代碼保留功能同時又和泛型兼容,Java允許使用泛型類而不提供任何類型參數,這會為類創建原始類型。原始類型與不使用泛型的遺留代碼是兼容的,但失去了泛型的類型安全性。本質上就是使用Object替換了類型參數所表示的類型。
class Solution<T> { private T ob; Solution(T ob) { this.ob = ob; } T getOb() { return ob; } public static void main(String[] args) { Solution solution = new Solution(new String("Hello World")); System.out.println(solution.getOb()); } }View Code
九、泛型類層次
使用泛型超類。
class A<T> { T a; A(T a) { this.a = a; } T getA() { return a; } } class B<T, V> extends A<T> { V b; B(T a, V b) { super(a); this.b = b; } V getB() { return b; } }View Code
泛型層次比較。
class Solution { public static void main(String[] args) { B<String, String> obB = new B<>("Hello", "World"); System.out.println(obB instanceof A<?>);//true } }View Code
當需要對泛型類進行強制轉換時,必須確保其相互兼容並且泛型參數相同。
十、擦除
為了兼容以前的Java版本,對Java語言的語法或虛擬機所做的任何修改必須避免破壞以前的代碼,所以Java使用擦除實現泛型。
擦除的工作原理:編譯Java代碼時,所有泛型信息被擦除。那麽必須使用他們的界定類型替換類型參數,如果沒有顯式指定界定類型,就會使用Object。之後進行適當的類型轉換以保持與類型參數所指定類型的兼容。這意味著允許時時沒有類型參數的,Java泛型只是一種源代碼機制,運行時的類型轉換必然會帶來開銷。
十一、模糊性錯誤
class Solution<T, V> { private T a; private V b; Solution(T a, V b) { this.a = a; this.b = b; } void set(T a) { this.a = a; } void set(V b) { this.b = b; } }View Code
上述代碼出現了模糊性錯誤,當T和V為相同類型時,無法確定調用的方法。
十二、限制
- 不能實例化類型參數的對象或數組。
- 靜態成員不能使用在類中聲明的類型參數。
- 泛型類不能擴展Throwable,即不能創建泛型異常類。
若要實例化泛型對象的數組,需要使用通配符。
class Solution<T> { public static void main(String[] args) { Solution<Integer>[] solutionsA = new Solution<Integer>[10];//錯誤 Solution<?>[] solutionsB = new Solution<?>[10];//正確 } }View Code
Java筆記:泛型