Kotlin中的泛型之协变和逆变

109 阅读3分钟

Kotlin中的泛型支持协变和逆变。接下来分别对它们进行介绍:

  1. 协变(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
  1. 逆变(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中的泛型有什么区别?

  1. 类型擦除
  • 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
}


  1. 类型投影
  • java使用通配符?结合extends和super来处理泛型的子类型和父类型

  • kotlin使用out和in

  • Kotlin 中的out A类似于 Java 中的? extends A,即泛型参数类型必须是A或者A的子类,用来确定类型的上限

  • Kotlin 中的in A类似于 Java 中的? super A,即泛型参数类型必须是A或者A的父类,用来确定类型的下限