Kotlin中的泛型支持协变和逆变。接下来分别对它们进行介绍:
- 协变(Covariant)
协变意味着可以使用子类型作为父类型的替代。在Kotlin中,为了支持协变,我们可以将out修饰符添加到泛型参数上。例如,让我们看一个用于生产者的接口:
interface Producer<out T> {
fun produce(): T
}
这个接口可以使用out修饰符,表示这是一个生产者,它只会产生类型T的值,而不会对其进行任何更改。因此,我们可以将子类型作为父类型的替代:
class AnimalProducer : Producer<Animal> {
override fun produce(): Animal {
println("animal")
return Animal()
}
}
class DogProducer : Producer<Dog> {
override fun produce(): Dog {
println("dog")
return Dog()
}
}
这里Dog是Animal的子类型,所以我们可以使用DogProducer作为类型为Producer的变量的值。因为我们知道我们总是可以期望DogProducer生产类型为Animal的值。
fun main(args: Array<String>) {
val producer: Producer<Animal> = DogProducer()
producer.produce()
}
输出dog
- 逆变(Contravariant)
逆变意味着可以使用父类型作为子类型的替代。在Kotlin中,为了支持逆变,我们可以将in修饰符添加到泛型参数上。例如,让我们看一个用于消费者的接口:
interface Consumer<in T> {
fun consume(item: T)
}
这个接口可以使用in修饰符,表示这是一个消费者,它只接受类型T的值,而不会返回任何值。因此,我们可以将父类型作为子类型的替代:
class AnimalConsumer : Consumer<Animal> {
override fun consume(item: Animal) {
// 消费Animal类型的值
if (item is Dog) {
println("item is dog")
} else {
println("item is animal")
}
}
}
class DogConsumer : Consumer<Dog> {
override fun consume(item: Dog) {
// 消费Dog类型的值
println("animal")
}
}
这里Animal是Dog的父类型,所以我们可以使用AnimalConsumer作为类型为Consumer的变量的值。因为我们知道我们总是可以期望AnimalConsumer会接受类型为Dog的值。
fun main(args: Array<String>) {
val consumer: Consumer<Dog> = AnimalConsumer()
val dog = Dog()
consumer.consume(dog)
}
输出item is dog
总之,Kotlin中的协变和逆变提供了更好的类型安全性和代码灵活性。使用它们可以确保类型转换是正确的,并且可以使程序更加健壮和易于维护。
Java中的泛型和Kotlin中的泛型有什么区别?
- 类型擦除
-
java中有泛型类型擦除机制,这意味着在编译过程中,泛型类型信息被移除,替换为它们的上限(默认情况下是
Object)。这会导致无法在运行时创建泛型的实例,以及在运行过程中无法检测泛型的具体类型 -
kotlin中虽然也有泛型类型擦除机制,但是kotlin编译器引入了内联特性,并结合reified关键字,可以使泛型具体化,在运行的过程中可以创建泛型实例,并能检测泛型的具体类型
// Kotlin中的泛型也不能直接用于基本类型,但是可以通过特定函数和类型参数化来保留类型信息
inline fun <reified T> checkType() {
if (T::class == Int::class) {
println("T is Int")
}
}
// Kotlin中的泛型实化
inline fun <reified T> isType(value: Any): Boolean {
return value is T
}
- 类型投影
-
java使用通配符?结合extends和super来处理泛型的子类型和父类型
-
kotlin使用out和in
-
Kotlin 中的out A类似于 Java 中的? extends A,即泛型参数类型必须是A或者A的子类,用来确定类型的上限
- Kotlin 中的in A类似于 Java 中的? super A,即泛型参数类型必须是A或者A的父类,用来确定类型的下限