kotlin in 与 out
kotlin 的in 与 out
kotlin: out in
java: ? extends ? super
public class Fanxing {
private static class A {
}
private static class B extends A {
}
private static void show(List<A> o) {
for (A a : o) {
System.out.println(a);
}
}
public static void main(String[] args) {
List<A> aList = new ArrayList<>();
List<B> bList = new ArrayList<>();
show(aList);
// 注意 下方 这个语句 会 报错 因为 Java 属于伪泛型 编译的时候会做类型擦除
// 为了安全起见 默认禁止 协变 与 逆变
show(bList);
}
}
-----------
show(Ljava/util/List;)V{
0 aload_0
1 invokeinterface #2 <java/util/List.iterator> count 1
6 astore_1
7 aload_1
8 invokeinterface #3 <java/util/Iterator.hasNext> count 1
13 ifeq 36 (+23)
16 aload_1
17 invokeinterface #4 <java/util/Iterator.next> count 1
22 checkcast #5 <Fanxing$A>
25 astore_2
26 getstatic #6 <java/lang/System.out>
29 aload_2
30 invokevirtual #7 <java/io/PrintStream.println>
33 goto 7 (-26)
36 return
}
我们看一下java的字节码会发现show()方法接收的参数为List类型并且注意字节码的第22行会尝试截断操作,这就是因为Java采用的是伪泛型,编译的时候会将泛型擦除,因此java默认不允许协变与逆变。(其实协变还好 但是 逆变 肯定 抛异常 程序 闪退)
我们将show方法改变如下
private static void show(List<? extends A> o) {
for (A a : o) {
System.out.println(a);
}
}
那么上面的代码就正常了。
但是 当我们 在show()方法中调用o.add(new A()); 就会报错,这也是为了安全。因为这是一个A子类的集合具体存储的值类型我们使用的时候无法判断,如果你向往里边添加元素,,那么大概率会发生类型截断异常,因为我们不知道数组的确切类型而且如果添加A也会造成父类强转为子类而抛出异常.
这就是协变 :Covariant
与? extends 相对的是 ?super这也是可以让 接受变量的范围扩大 但是 扩大的方向 恰好相反。
private static void show(List<B> o) {
for (B b : o) {
System.out.println(b);
}
}
public static void main(String[] args) {
List<A> aList = new ArrayList<>();
List<B> bList = new ArrayList<>();
// 发生错误的语句
show(aList);
show(bList);
}
现在 上面的程序 依然报错 但是 错误的原因 却改变了.
我们 要将show()方法改为show(List<? super B> o)这样就可以了。
逆变:Contravariant
但是 这也有个限制 就是 我们 在 show方法里边 不可以 调用show()方法带有泛型返回值的方法比如get()方法.
这也很好理解,因为 我们 在 show()方法里边不知道这个List的内容,我们调用它的get()方法但是却不知道他是什么类型这样使用非常不安全。
回到Kotlin 当中的in 与out
他们与java中的?extends 以及 ?super相对应
out: 只能被当做泛型类的输出类型:只能被使用(输出了才可以被使用)
in:只能当做泛型类的输入类型:只能被修改(输入给泛型类,让泛型类就该自身数据)
java中变量声明可以使用<?> 这就相当于<? extends object> 而kotlin泛型之中的<*>相当于
open class A<T:Any> 泛型 类型声明之中的上界 区别于 泛型声明
open class A<out T : Number> {
fun get() {
}
}
fun main() {
val a: A<*> = A<Int>()
// 相当于 不等于 val aa:A<out Object> = A<Int>()
val aa:A<out Number> = A<Int>()
}
java的多重上界
class A extends B & c{}
而在kotlin之中多重上界的表现形式略微不同
open class A<T> where T:Number,T:AA{
fun get() {
}
}
kotlin泛型的特殊地方是泛型可以加上reified关键字就可以使用泛型T,比如
inline fun <reified T> show() {
println(T::class)
}
要注意的是 reified关键字必须与inline 一块使用