• 泛型总结


    一 容器类:请取出同一类型的物品

    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时将导致编译错误。

    二 泛型通配符

    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 类或者接口 & 接口 & 接口 ...
  • 相关阅读:
    Java中的24种设计模式与7大原则
    Red Hat Enterprise Linux 6安装步骤
    jquery之获取当前时间和月份加1
    新手看过来:随车千斤顶怎么用?
    空挡滑行和带挡滑行究竟谁省油 误区要知道
    怎么开手动档轿车?这么开让你再也不想开自动档
    FindBugs插件的安装与使用
    图解Eclipse中安装和使用FindBugs
    新手开车上高速 不懂这些安全细节非常危险
    酒桌游戏
  • 原文地址:https://www.cnblogs.com/linzhanfly/p/9761803.html
Copyright © 2020-2023  润新知