泛型 上界通配符 下界通配符 协变 逆变
一:介绍(Java完成100%,Kotlin完成10%)
AndroidWork:主要对泛型进行解释说明 需要准备一些基础类如下有 Food Meat Fruit Apple Banana 如图说明他们关系
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";
}
}
二:关键字说明
-
泛型 泛型是一种不需要确定具体数据类型的定义,编译会自动对泛型进行转换,可以方便我们在编程的时候减少很多重复代码,同时增加安全性(区别于Object,object是超级父类)
-
= Class 下定义时以及初始化时说明,=左边为 下定义时 ,=右边为 实例化时 ,下一行之后都是 初始化后
-
上界通配符(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)
-
-
下界通配符(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)
参考文献
表格图解