Java 泛型 (? extends T 上界通配符) +(? super T 下界通配符) +Kotlin 泛型 (out 协变)+(in逆变)浅析

422 阅读4分钟

泛型 上界通配符 下界通配符 协变 逆变

一:介绍(Java完成100%,Kotlin完成10%)

AndroidWork:主要对泛型进行解释说明 需要准备一些基础类如下有 Food Meat Fruit Apple Banana 如图说明他们关系

type.png

    public abstract class Food {
        abstract String name();
    }

    public class Meat extends Food {
        @Override
        String name() {
            return "Meat";
        }
    }

    public class Fruit extends Food {
        @Override
        String name() {
            return "Fruit";
        }
    }

    public class Apple extends Fruit {
        @Override
        String name() {
            return "Apple";
        }
    }

    public class Banana extends Fruit {
        @Override
        String name() {
            return "Banana";
        }
    }

二:关键字说明

  1. 泛型 泛型是一种不需要确定具体数据类型的定义,编译会自动对泛型进行转换,可以方便我们在编程的时候减少很多重复代码,同时增加安全性(区别于Object,object是超级父类)

  2. = Class 下定义时以及初始化时说明,=左边为 下定义时 ,=右边为 实例化时 ,下一行之后都是 初始化后

  3. 上界通配符(Java: <? extends T> 和 Kotlin: < out T > 协变 相似)

    • Java: <? extends T> ( 实例化时 泛型中的类型只能是T以及子类,同时可以存输入参数值; 初始化后 只能取输出返回值,不可以再存输入参数值)

      下定义时 的泛型 ?,在 实例化时 泛型只能是当前类T及其子类,在 初始化后 只能输出返回值,不能在实 初始化后 存放数据,存放数据需要放在 实例化时 即是 Class(){{存放数据}} ,代码如下

        List<? extends Food> foods = new ArrayList<Fruit>() {{
            //数据初始化
            /* add(new Food(){
                 @Override
                 String name() {
                     return "Food";
                 }
             });*///报错
             add(new Fruit());
             add(new Apple());
             // add(new Meat());报错
         }};
         // 初始化再存放数据时会报错
         // foods.add(new Fruit());  报错
         // foods.add(new Apple());  报错
         // foods.add(new Meat());   报错
      
         //获取输出实例 默认是Object
         Object obj = foods.get(0);
         
         //获取输出实例 当需要指定类型的时候需要进行强转,但是强转就可能会带来类型转换异常的错误(get(1)是Apple)
         Fruit fruit = (Fruit) foods.get(0);
      

      如上代码清晰说明了泛型?,
      那么问题来了上界通配符的上界是什么意思会在泛型中有什么影响?

      List<? extends Food> foods = new ArrayList<Fruit>();
      

      例子中的? extends Food 下定义时 上界 是Food, 实例化时 泛型是Fruit,Fruit是Food的子类
      结果 实例化时 存放数据实例类型受限于 实例化时 的泛型是Fruit,只能存放Fruit及其子类,而不是 下定义时 的泛型Food 故此若将 实例化时 的泛型

      改为Food如下

      List<? extends Food> foods = new ArrayList<Food>()
      
      List<? extends Food> foods = new ArrayList<Food>() {{
            add(new Food() {
                @Override
                String name() {
                    return "Food";
                }
            });
            add(new Fruit());
            add(new Apple());
            add(new Meat());
      }};
      

      此时Food及其子类全都可以在 实例化时 存放,不会出现报错信息
      总结: 实例化时 的泛型受限于 下定义时 的泛型, 实例化时 存放数据类型受限于 实例化时 泛型

    • Kotlin 中的out:T 协变 可输出返回值fun get():T,不可以输入参数fun set(t:T)

  4. 下界通配符(Java:<? super T> 和Kotlin: < in T > 逆变 相似)

    • Java:<? super T> ( 实例化时 泛型中的类型只能是T及其父类, 实例化时 初始化后 两个过程均可以存取实例)

    下定义时 的泛型? 在 实例化时 和 初始化后 添加对象类型限制是不同的, 初始化后 添加对象只能是 下定义时 T及其子类, 实例化时 添加对象时的类型只能是 实例化时 类型及其子类的我们可以将两个看作都有各自的作用域代码如下

          List<? super Fruit> fruits = new ArrayList<Food>() {{
             //add(new Object()) 报错
             add(new Apple());
             add(new Food() {
                 @Override
                 String name() {
                     return null;
                 }
             });
             //正常添加 Meat和Fruit均为Food的子类,但此时的作用域却是<font color= red> 实例化时 </font>Food类型下,而不受<font color= red> 下定义时 </font> T类型限制
             add(new Meat());
             add(new Fruit());
             Object object = get(0);
         }};
         // fruits = new ArrayList<Apple>(); 报错
         // fruits = new ArrayList<Banana>(); 报错
         fruits.add(new Apple());
         //fruits.add(new Food()); 报错
         fruits.add(new Banana());
         fruits.add(new Fruit());
         //报错 <font color= red> 初始化后 </font> 作用域发生改变,添加对象受到<font color= red> 下定义时 </font> T类型限制  
         //fruits.add(new Meat());
         Apple apple = (Apple) fruits.get(0);
         Object object = fruits.get(0);
    
    • Kotlin 中的in:T 逆变 不可输出返回值fun get():T,可输入参数fun set(t:T)

参考文献

  1. Java 泛型中extends和super的区别
  2. Java 泛型中的extends和super理解
  3. Kotlin (out)协变和(in)逆变
  4. URL

表格图解