- Kotlin进阶实战
- 沈哲 易庞宙编著
- 822字
- 2025-02-20 21:22:45
6.2.2 型变
型变是指类型转换后的继承关系。
Kotlin的型变分为逆变、协变和不变。
1.协变
如果A是B的子类型,并且Generic<A>是Generic<B>的子类型,那么Generic<T>可以称为一个协变类。
(1)Java上界通配符<? extends T>
Java的协变通过上界通配符实现。
如果Dog是Animal的子类,但List<Dog>并不是List<Animal>的子类。例如,下面的代码会在编译时报错:
List<Animal> animals = new ArrayList<>(); List<Dog> dogs = new ArrayList<>(); animals = dogs; //incompatible types
而使用上界通配符之后,List<Dog>变成了List<? extends Animal>的子类型,即animals变成了可以放入任何Animal及其子类的List。因此,下面的代码编译是正确的:
List<? extends Animal> animals = new ArrayList<>(); List<Dog> dogs = new ArrayList<>(); animals = dogs;
(2)Kotlin的关键词out
上述代码改成Kotlin的代码:

居然没有编译报错?其实,Kotlin的List跟Java的List并不一样。Kotlin的List源码中使用了out,out相当于Java的上界通配符。

当类的参数类型使用了out之后,该参数只能出现在方法的返回类型中。
(3)@UnsafeVariance
我们发现List的contains()、containsAll()、indexOf()和lastIndexOf()方法中,入参均出现了范型E,并且使用@UnsafeVariance修饰。
这里是由于@UnsafeVariance的修饰,才打破了out使用的限制,否则编译会报错。
2.逆变
如果A是B的子类型,并且Generic<B>是Generic<A>的子类型,那么Generic<T>可以称为一个逆变类。
(1)Java下界通配符<? super T>
Java的逆变通过下界通配符实现。下面的代码因为是协变的,无法添加新的对象。编译器只能知道类型是Animal的子类,并不能确定具体类型是什么,因此无法验证类型的安全性。
List<? extends Animal> animals = new ArrayList<>(); animals.add(new Dog()); //compile error
使用下界通配符之后,代码顺利编译通过:
List<? super Animal> animals = new ArrayList<>(); animals.add(new Dog());
其中,? super Animal表示Animal及其父类。所以animals可以接受所有Animal的子类添加至该列表中。
Java的上界通配符和下界通配符符合PECS(Producer Extends,Consumer Super)原则。如果参数化类型是一个生产者,则使用<? extends T>;如果它是一个消费者,则使用<? super T>。
其中,生产者表示频繁往外读取数据T,而不从中添加数据。消费者表示只往里插入数据T,而不读取数据。
可以用下面的公式帮助记忆:
produce = output = out. consume = input = in.
(2)Kotlin的关键词in
in相当于Java下界通配符。

当类的参数类型使用了in之后,该参数只能出现在方法的入参中。
3.不变
默认情况下,Kotlin中的泛型类是不变的。这意味着它们既不是协变的,又不是逆变的。例如MutableList,泛型没有使用in、out,它可读可写。前面讲到的Kotlin数组也是不变的。