代码复现
先看下面一段代码
java
List<String> strList = new ArrayList<>();
List<Object> objList = strList; // 报错
乍一看好像没什么问题,但为什么追报错呢?
先别急,看下面一段代码
java
List<Integer> intList = new ArrayList<>();
List<Object> objList = intList;
objList.add("hello"); // 添加某个未知类型
Integer i = intList.get(0); // 结果一定是Integer吗
偷偷把str放到objList里了,但又从intList拿出来了?
看来你应该知道编译器为什么报错了吧,就是为了防止小白偷偷添加一些奇怪的东西
如何防止
简单啊,只要禁止add()就行了
java
List<Integer> intList = new ArrayList<>();
List<? super Integer> objList = intList;
objList.add("hello"); // 报错
Integer i = intList.get(0);
我们通过**? super Intege**限定了List的下界,此时objList只能存入某个Integer的父类以及父类的子类。但我们只能推断出Integer是该父类的一个子类,所以放入String会报错(String是Object的子类,但不一定是Integer某个父类的子类)。
所以编译器报错是为了防止我们乱添加?
太麻烦了?
java
// 定义一个接口,里面只有get()
interface MyList<T> {
T get();
}
// 实例
MyList<String> strList = () -> null;
MyList<Object> objList = strList; // 拨错
MyList<? extends String> objList2 = strList; // 正常
编译器报错是为了防止我们乱添加,但MyList接口里没有add()之类的添加方法,也要为了不报错而改成**? extends String**,???
Kotlin的优化
kotlin
// 接口只有get()方法
interface MyList<out T> {
fun get(): T
}
val strList: MyList<String> = object : MyList<String> {
override fun get(): String {
return "Hello"
}
}
val objList: MyList<Any> = strList // 正常
kotlin在语法方面做出的优化,当我们确定不会出现**偷偷把str放到objList里了,但又从intList拿出来了?**这种情况时,可以使用out关键字标记为只读不写