• 作者:老汪软件技巧
  • 发表时间:2024-09-11 10:02
  • 浏览量:

Kotlin 中的泛型(Generics)提供了一种让类、接口和函数可以根据类型参数进行操作的机制。它允许编写更加通用和可重用的代码,而不必限定在特定的类型上。Kotlin 的泛型机制与 Java 类似,但引入了一些 Kotlin 特有的功能,比如 型变(variance) 和 投影(projection) ,使得泛型的使用更加灵活。

以下是 Kotlin 泛型的详细讲解:

1. 泛型的基础概念

泛型允许我们定义类型参数,使代码能够在编译时处理多种数据类型。一个泛型类或函数的定义如下:

1.1 泛型类

你可以通过在类名后面使用尖括号 来定义一个泛型类。例如,下面是一个简单的泛型类 Box,它可以包含任何类型的值:

kotlin
复制代码
class Box<T>(val value: T)
val intBox = Box(1)  // Box
val stringBox = Box("Hello")  // Box

这里 T 是类型参数,表示 Box 可以容纳任何类型的数据。Box 的实例化将会推断具体的类型参数。

1.2 泛型函数

同样,你可以为函数定义泛型:

kotlin
复制代码
fun  singletonList(item: T): List {
    return listOf(item)
}
val intList = singletonList(1)  // List
val stringList = singletonList("Hello")  // List

泛型函数在函数名之前声明了类型参数 ,并且可以在函数体内使用这个类型。

2. 型变(Variance)

Kotlin 中的型变是泛型的一个重要概念,用于处理子类型关系。Kotlin 的型变比 Java 更加严格和安全。它分为三种类型:

2.1 协变(Covariance) :out 关键字

协变允许你将泛型类型声明为可以向父类方向兼容。用 out 关键字声明的泛型只能作为返回类型(输出),不能作为输入参数。

kotlin
复制代码
interface Producer<out T> {
    fun produce(): T
}
class Animal
class Dog : Animal()
fun demo(producer: Producer<Animal>) {
    val animal: Animal = producer.produce()
}
val dogProducer: Producer = object : Producer {
    override fun produce(): Dog {
        return Dog()
    }
}
demo(dogProducer)  // 由于 Producer 是协变的,它可以赋值给 Producer

通过 out,Producer 可以被当作 Producer 来使用。这确保了类型安全,同时允许更灵活的泛型使用。

2.2 逆变(Contravariance) :in 关键字

逆变允许你将泛型类型声明为可以向子类方向兼容。用 in 关键字声明的泛型只能作为输入参数,不能作为返回类型。

kotlin
复制代码
interface Consumer<in T> {
    fun consume(item: T)
}
fun demo(consumer: Consumer<Dog>) {
    consumer.consume(Dog())
}
val animalConsumer: Consumer = object : Consumer {
    override fun consume(item: Animal) {
        println("Consumed an animal")
    }
}
demo(animalConsumer)  // Consumer 可以用于 Consumer

通过 in,Consumer 可以用于 Consumer 的场景,因为 Consumer 可以消费 Dog 或任何 Animal 的子类。

2.3 不变(Invariant)

泛型list_泛型调用_

如果没有使用 out 或 in 关键字,泛型是不变的,这意味着子类型和父类型之间没有自动的兼容关系。

kotlin
复制代码
class Box<T>(val value: T)
val box: Box = Box(Dog())  // 错误,Box 不能赋值给 Box

3. 泛型约束

Kotlin 允许对泛型进行约束,以限制类型参数可以接受的类型。最常见的约束是使用 : Any 或特定的父类来限制类型。

3.1 限制类型参数

通过 where 关键字,你可以进一步限制类型参数可以接受的多个约束条件:

kotlin
复制代码
fun > sort(list: List<T>) {
    // T 必须是 Comparable 的子类
}
fun  copyWhenGreater(list: List<T>, threshold: T): List
    where T : CharSequence, T : Comparable {
    return list.filter { it > threshold }
}

这里的泛型 T 被限制为 Comparable 或者其他指定的接口或类。

4. 泛型类型投影(Type Projections)

Kotlin 提供了 类型投影 以进一步控制泛型的使用方式。类型投影分为:

4.1 协变投影(out projection)

当你只打算从集合中读取数据时,可以使用协变投影来限制只能获取值,而不能设置值:

kotlin
复制代码
fun printValues(list: List<out Number>) {
    for (number in list) {
        println(number)
    }
}

List 表示该列表中的元素是 Number 或 Number 的子类型,但不能往里面添加任何数据。

4.2 逆变投影(in projection)

当你只打算将数据写入集合时,可以使用逆变投影来限制只能设置值,而不能读取值:

kotlin
复制代码
fun addTo(list: MutableList<in String>) {
    list.add("Hello")
}

MutableList 表示你可以向列表中添加 String 或 String 的子类,但不能保证可以从中读取特定类型的数据。

5. 泛型擦除(Type Erasure)

与 Java 类似,Kotlin 的泛型也是在运行时被 类型擦除(Type Erasure) 的。这意味着在运行时,泛型类型信息会被丢弃。例如,List 和 List 在运行时都是 List。

不过,Kotlin 提供了 reified(具象化)类型参数,允许在内联函数中保留泛型的类型信息。使用 reified 可以避免类型擦除带来的问题:

kotlin
复制代码
inline fun <reified T> isOfType(value: Any): Boolean {
    return value is T
}
println(isOfType("Hello"))  // true
println(isOfType<Int>("Hello"))  // false

6. 泛型实战场景总结

Kotlin 的泛型提供了灵活而强大的类型安全机制,通过 型变、类型约束 和 投影,你可以更好地控制泛型的行为,确保代码的可读性和可维护性。泛型让代码更加通用和可重用,同时保持类型安全。


上一条 查看详情 +没有了
下一条 查看详情 +没有了