缘起
今天有同事问以下代码的使用
List<String> list=new ArrayList<>();
setData(list); //异常报错:类型错误
setNewData("LLL");//正常使用
public void setData(List<Object> data){
}
public void setNewData(Object data){
}
思想建立
String extends Object
- 里氏替换原则(继承): String 和 Object 单独使用没问题
- 协变: 换一个使用环境依然遵守继承关系: java 数组是协变的,
- 逆变:兼容方向发生了转变,传入继承者,可兼容父类
- 不变:在新的环境关系断绝: list 列表是不变的,泛型是不变的
- 通配符:
<?>,它代表任意类型通配符
原因
public void setData(List<Object> data)
在方法中为list 泛型 失去了继承关系 为不变,所以在开头的 使用中编译器是不能通过的
java 为什么会这样设计:
保证类型的安全性 List<Object> 有很多子类,防止后续类型取出发生类型转变错误,比如数组未知的情况下就会出现类型转换失败,形成运行时错误;
Object[] objects=new String[3];
objects[0]=1;
objects[1]="2";
int num= (int) objects[1];//运行错误
使用
但在开发过程中我们很多场景是需要类似继承的扩展,那不变的list 和泛型是不是隔绝了协变和逆变?
Java 为了满足部分场景提供了? extends 和 ? super
- 消费者(TextView extends View):
List<? extends View>是List<TextView>的父类以提供协变的能力,函数的拿到的数据只能取不可修改,增加类型安全
List<TextView> list =new LinkedList<TextView>();
list.add(new TextView(this));
setData(list);// 正常使用
public void setData(List<? extends View> list){
// list.add(new TextView(this)); // 禁止添加 保证类型安全
TextView textView= (TextView) list.get(0);
}
- 生产者:
List<? super TextView>是List<View>的子类提供逆变能力,并可以添加数据
List<View> list1 =new LinkedList<>();
list1.add(new TextView(this));
setData1(list1);
public void setData1(List<? super TextView> list){
list.add(new TextView(this));
TextView textView= (TextView) list.get(0);
}