泛型總結
一 容器類:請取出同一型別的物品
1 不使用泛型
// A是B的父類 List list = new ArrayList<>(); list.add(new A()); list.add(new A()); list.add(new String()); System.out.println((A)list.get(0)); System.out.println((A)list.get(1)); // 編譯時不報錯,執行時報錯:java.lang.ClassCastException System.out.println((A)list.get(2));
2 不恰當泛型
// A是B的父類 List<A> list = new ArrayList<>(); list.add(new A()); list.add(new B()); // list.add(new String()); // 加上時,編譯會報錯 System.out.println((B)list.get(0)); // 編譯時不報錯,執行時報錯:java.lang.ClassCastException System.out.println((B)list.get(1));
- 補救方案:先進行型別判斷
A a1 = list.get(1); if(a1.getClass() == B.class){ System.out.println((B)a1); }
3 正確使用泛型
// A是B的父類 List<A> list = new ArrayList<>(); list.add(new A()); list.add(new B()); // 如果把B放入A的容器中,就把B當成A使用,不要想著強轉回來使用了。 System.out.println(list.get(0)); System.out.println(list.get(1));
4 總結
- 沒有泛型時,不能依賴編譯器的語法檢查,由於List 等同於 List<Object>。
- 泛型能夠給編譯器提供型別檢查。
- 父類容器雖然可以放入子類,但取出來儘量不要還原回子類進行使用,費時費力。
二 泛型萬用字元
1 準備
public class Main { static class A { } static class B extends A { } static class C<T>{ private T t; public C(){ } public T getT() { return t; } public void setT(T t) { this.t = t; } } }
2 測試
(1)型別擦除和型別強轉
- java
public class Main { public static void main(String[] args) { C<A> cA = new C<>(); // 型別強轉:這就是為什麼1.1節中編譯不報錯,執行時報錯的原因。編譯時型別擦錯,並不進行檢查。 // Object obj = cA.getT(); // A t = (A)obj; A t = cA.getT(); } }
- 位元組碼
public static void main(java.lang.String[]); Code: 0: new#2// class Main$C 3: dup 4: invokespecial #3// Method Main$C."<init>":()V 7: astore_1 8: aload_1 9: invokevirtual #4// Method Main$C.getT:()Ljava/lang/Object; 12: checkcast#5// class Main$A 15: astore_2 16: return
(2)C<A>和C<B>是相同型別嗎?
public static void main(String[] args) { C<A> cA = new C<>(); C<B> cB = new C<>(); // 為什麼Class<? extends C> 而不是Class<C> Class<?> Class<? super C> ? Class<? extends C> aClass = cA.getClass(); Class<? extends C> bClass = cB.getClass(); boolean b = aClass == bClass; // true }
(3)萬用字元作為入參
-
demo1
public static void main(String[] args) { C<A> cA = new C<>(); C<B> cB = new C<>(); test(cA); test(cB); // 編譯時錯誤:Error: java: 不相容的型別 } public static void test(C<A> c){ A a = c.getT(); }
-
demo2:
C<Object>
不是C<A>
的父類
public static void main(String[] args) { C<A> cA = new C<>(new A()); C<B> cB = new C<>(new B()); test(cA); // 編譯時錯誤:Error: java: 不相容的型別 test(cB); // 編譯時錯誤:Error: java: 不相容的型別 } public static void test(C<Object> c) { Object t = c.getT(); }
-
demo3:
C<?>
接收所有型別
public static void main(String[] args) { C<A> cA = new C<>(new A()); C<B> cB = new C<>(new B()); test(cA); test(cB); } public static void test(C<?> c) { // 或者C c也行 Object t = c.getT(); }
-
demo4:定義上界限,泛型是A或者繼承A即可。
public static void main(String[] args) { C<A> cA = new C<>(new A()); C<B> cB = new C<>(new B()); test(cA); test(cB); } public static void test(C<? extends A> c) { A t = c.getT(); }
-
demo5:定義下界限,泛型是A或A的父類。
public static void main(String[] args) { C<A> cA = new C<>(new A()); C<B> cB = new C<>(new B()); test(cA); test(new C<Object>()); test(cB); // 編譯時報錯: } public static void test(C<? super A> c) { Object t = c.getT(); }
(4)統配符作為返回值
-
demo1:可以修改,但是需要是泛型是A或者A的子類.
public static void main(String[] args) { C<A> test = test(); test.setT(new A()); test.setT(new B()); test.setT(new Object()); // 編譯錯誤 } public static C<A> test() { C<A> cA = new C<>(new A()); return cA; }
-
demo2:?返回,無法修改,只能讀取
public static void main(String[] args) { C<?> test = test(); test.setT(new A()); // 編譯錯誤 test.setT(new B()); // 編譯錯誤 test.setT(new Object()); // 編譯錯誤 } public static C<?> test() { C<A> cA = new C<>(new A()); return cA; }
-
demo3:?extends 返回,無法修改,只能讀取,和demo2相同,但是類中的泛型是什麼。
public static void main(String[] args) { C<? extends A> test = test(); test.setT(new A()); // 編譯錯誤 test.setT(new B()); // 編譯錯誤 test.setT(new Object()); // 編譯錯誤 } public static C<? extends A> test() { C<A> cA = new C<>(new A()); return cA; }
-
demo4:可以修改,但泛型需要是A或者繼承A的類,和demo1相同。
public static void main(String[] args) { C<? super A> test = test(); test.setT(new A()); test.setT(new B()); test.setT(new Object()); // 編譯錯誤 } public static C<? super A> test() { C<A> cA = new C<>(new A()); return cA; }
-
結論:
- 泛型返回時,如果可以修改則指定泛型型別即可,如demo1
- 泛型返回時,如果不可以修改,則使用demo3,這樣可以知道泛型型別是什麼。這也是為什麼c.getClass()方法返回Class<? extends C>的原因。
(5)泛型多繼承
static class A {} static interface D {} // extends 類或介面 & 介面 & 介面 ... static class C<T extends A & D & Comparable> { private T t; }
3 總結
- 編譯時進行型別擦除 ,取出時進行型別強轉。
- Class型別相同,但是泛型型別不同。
-
傳參
:Class型別相同情況下
- ?:接收所有型別的泛型型別
- ? extends T:接收泛型型別為T或者T的子類
- ? super T:接收泛型型別為T或者T的父類
- T:只接收泛型型別為T
-
返回
:都支援讀取
- ?:無法修改
- ? extends T:無法修改,但知道泛型型別為T
- ?super T:支援修改為T或T的子類的物件
- T:支援修改為T或T的子類的物件
- 泛型繼承 :T extends 類或者介面 & 介面 & 介面 ...