泛型總結

一 容器類:請取出同一類型的物品

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<?>,添加泛型時,該方法的屬性將多一個Signature;。
  • 泛型可以給編譯器提供類型檢查。
  • 父類容器雖然能夠放入子類,但取出來儘可能不要還原回子類進行使用,費時費力。

注:一個接受Collection<?>的方法和一個接受List的方法構成重載,調用傳參爲ArrayList時將致使編譯錯誤。java

二 泛型通配符

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 類或者接口 & 接口 & 接口 ...
相關文章
相關標籤/搜索