类(Class) 与类型(Type)
Kotlin 中类和类型是不一样的概念,如下图:
型变
型变是指类型转换后的继承关系。Kotlin 的型变分为逆变、协变和不变。
Kotlin 和 Java 的协变
在 Java 中用通配符 ? extends T 表示协变,extends 限制了父类型 T。
在 Kotlin 中关键字 out T 表示协变,含义和 Java 一样
// kotlin
val numbers: MutableList<out Number> = ArrayList<Int>()
// Java
List<? extends Number> numbers = new ArrayList<Integer>();
协变通配符 ? extends Number 或者 out Number 表示接受 Number 或者 Number 子类型为对象的集合,协变放宽了对数据类型的约束。
但是放宽是有代价的,调用 add() 方法会编译失败,虽然协变放宽了对数据类型的约束,可以接受 Number 或者 Number 子类型为对象的集合,但是代价是 无法添加元素,只能获取元素,因此协变只能作为生产者,向外提供数据。
// Koltin
val numbers: MutableList<out Number> = ArrayList<Int>()
numbers.add(1)
// Java
List<? extends Number> numbers = new ArrayList<Integer>();
numbers.add(1)
因为 ? 表示未知类型,所以编译器也不知道会往集合中添加什么类型的数据,因此索性不允许往集合中添加元素。
但是如果想让上面的代码编译通过,想往集合中添加元素,这就需要用到逆变了。
Kotlin 和 Java 的逆变
在 Java 中用通配符 ? super T 表示逆变,其中 ? 表示未知类型,super 主要用来限制未知类型的子类型 T,比如 ? super Number,只要声明时传入是 Number 或者 Number 的父类型都可以
在 Kotlin 中关键字 in T 表示逆变,含义和 Java 一样
逆变的例子:
// Kotlin
val numbers: MutableList<in Number> = ArrayList<Number>()
numbers.add(100)
val item: Int = numbers.get(0)
// Java
List<? super Number> numbers = new ArrayList<Number>();
numbers.add(100);
int item = numbers.get(0);
逆变通配符 ? super Number 或者关键字 in 将继承关系颠倒过来,主要用来限制未知类型的子类型,在上面的例子中,编译器知道子类型是 Number,因此只要是 Number 的子类都可以添加。add是可以添加成功,但是get无法添加成功。
实战代码:
//泛型类定义
class Generic<T> (private val obj: T) { //需求: 万能输入器
fun show() = println("输出:" + obj)
}
data class Student(val name: String, val age: Int, val sex: Char)
data class Teacher(val name: String, val age: Int, val sex: Char)
fun showGeneric() {
val stu1 = Student("张三", 10 ,'0')
val stu2 = Student("李四", 10 ,'0')
val tea1 = Teacher("miss", 25, '1')
Generic(stu1).show()
Generic(stu2).show()
Generic(tea1).show()
Generic(String("lili".toByteArray())).show()
}
class Dinamic<T> (vararg objects: T) { //定义泛型的动态参数
//out 我们的T只能被读取,不能被修改
val objectArray: Array<out T> = objects //因为T为任意类型,需要集合承接,需要定义一个下界,即协变
fun showObj(index: Int) : T ? = objectArray[index].takeIf { it is Int }
}
fun showVararg() {
Dinamic(10, "haha", Student("", 1, '0')).showObj(0)
}
// Out In
//生产者 out T 协变 使用out T 数据就是只读的,不能被修改
interface Producer<out T> {
// fun consumer(item: T) //进行消费
// //这里是out修饰T,无法对T对象进行操作使用,所以编译不通过
fun producer(): T
}
//消费者 in T 逆变 使用in T 数据就是只读的,不能被修改
interface Consumer<in T> {
fun consumer(item: T)
//不能被读取 (编译不通过)
// fun producer(): T
}
//生产者 & 消费者 T 默认情况下,是不变
interface ProducerAndConsumer <T> {
fun consumer(item: T) //能被读取
fun producer(): T //能被修改
}
open class Animal
open class Human: Animal()
class ProduceClass: Producer<Animal> {
override fun producer(): Animal {
println("生产者 Animal")
return Animal()
}
}
class ProduceClass2: Producer<Human> {
override fun producer(): Human {
println("生产者 Human")
return Human()
}
}
class ComsumerClass: Consumer<Animal> {
override fun consumer(item: Animal) {
println("消费者 Animal")
}
}
class ComsumerClass2: Consumer<Human> {
override fun consumer(item: Human) {
println("消费者 Human")
}
}
fun showOutIn() {
//泛型默认情况是:泛型的子类对象 不可以赋值给 泛型的父类对象
// List<CharSequence> list2 = new ArrayList<String>() //报错
// List<? extends CharSequence> list2 = new ArrayList<String>() //正确 声明泛型是CharSequence或它的子类就可以
val p1: Producer<Animal> = ProduceClass()
val p2: Producer<Animal> = ProduceClass2() // ProduceClass2传递的是Human,因为 T是out修饰,对应java的 ? extend T,只要类型是Animal的子类都可以
p2.producer()
val c1: Consumer<Human> = ComsumerClass() //ComsumerClass传递的事Animal, 因为 T是in修饰,对应java的 ? super T,只要类型是Human的父类都可以
c1.consumer(Human())
val c2: Consumer<Human> = ComsumerClass2()
}
参考:
https://zhuanlan.zhihu.com/p/531581456
https://www.jianshu.com/p/056d53e699ce