Kotlin中的泛型与型变
发布时间: 2024-01-21 14:47:01 阅读量: 17 订阅数: 12
# 1. 泛型基础知识
## 1.1 什么是泛型?
泛型是一种抽象类型,它将类型参数化,使得我们在使用类、接口、方法时可以传入具体的类型,从而实现代码的复用和类型安全。
## 1.2 Kotlin中的泛型概念
在Kotlin中,使用泛型可以在类、接口、函数中声明类型参数,并在使用时指定具体类型,例如:
```kotlin
// 声明一个泛型类
class Box<T>(t: T) {
var value = t
}
// 使用泛型类
val box: Box<Int> = Box(5)
```
## 1.3 泛型类与泛型函数
Kotlin中不仅可以创建泛型类,还可以声明泛型函数。通过泛型函数,可以实现在函数内部使用泛型类型,例如:
```kotlin
// 声明一个泛型函数
fun <T> singletonList(item: T): List<T> {
return listOf(item)
}
// 使用泛型函数
val list: List<Int> = singletonList(1)
```
在下一章节中,我们将深入探讨Kotlin中的型变(Variance)。
# 2. Kotlin中的型变
### 2.1 协变、逆变和不变
在Kotlin中,泛型默认是不变的。不变意味着类型参数不能被替代为它们的子类型或父类型。然而,在某些情况下,我们可能需要允许类型参数的协变或逆变。
在协变(covariance)中,泛型参数可以被展开为其子类型。这意味着,如果`B`是`A`的子类型,那么`List<B>`就是`List<A>`的子类型。在Java中,我们通常使用通配符(`?`)来实现协变。
```
// Java代码示例
List<? extends A> list = new ArrayList<>();
```
在Kotlin中,我们使用`out`关键字来声明类型参数是协变的。下面是一个使用协变的例子:
```kotlin
// Kotlin代码示例
interface Producer<out T> {
fun produce(): T
}
class AnimalProducer : Producer<Animal> {
override fun produce(): Animal {
return Animal()
}
}
fun main() {
val producer: Producer<Animal> = AnimalProducer()
val animal: Animal = producer.produce()
}
```
在逆变(contravariance)中,泛型参数可以被展开为其父类型。这意味着,如果`B`是`A`的父类型,那么`Consumer<A>`就是`Consumer<B>`的父类型。在Java中,我们通常使用通配符(`? super B`)来实现逆变。
```
// Java代码示例
Consumer<? super B> consumer = new Consumer<>()
```
在Kotlin中,我们使用`in`关键字来声明类型参数是逆变的。下面是一个使用逆变的例子:
```kotlin
// Kotlin代码示例
interface Consumer<in T> {
fun consume(item: T)
}
class AnimalConsumer : Consumer<Animal> {
override fun consume(item: Animal) {
println("Consuming animal")
}
}
fun main() {
val consumer: Consumer<Any> = AnimalConsumer()
consumer.consume(Animal())
}
```
### 2.2 Kotlin中的类型投影
在Kotlin中,我们还可以使用类型投影(type projection)来解决泛型类型的协变或逆变问题。类型投影可以更灵活地处理泛型参数。
星投影是一种特殊的类型投影,用星号(`*`)表示。在类型投影中,我们可以使用`*`来代替具体的类型。下面是一个使用星投影的例子:
```kotlin
// Kotlin代码示例
fun printItems(list: List<*>) {
for (item in list) {
println(item.toString())
}
}
fun main() {
val list: List<Any> = listOf("apple", 1, true)
printItems(list)
}
```
### 2.3 在泛型中使用型变
Kotlin中的型变可以应用于类、接口、函数和属性。通过在类型参数前添加`out`或`in`关键字,我们可以声明类型参数的协变或逆变。
在类或接口中使用型变时,我们需要根据类型参数在构造函数中的使用情况来决定使用`out`或`in`。
在函数中使用型变时,我们需要根据参数类型的使用情况来决定使用`in`还是`out`。
在属性中使用型变时,我们需要根据属性类型在getter和setter方法中的使用情况来决定使用`in`还是`out`。
```kotlin
// Kotlin代码示例
class Box<out T>(private val value: T) {
fun getValue(): T {
return value
}
}
fun setValue(box: Box<in String>, value: String) {
box.setValue(value)
}
val animalBox: Box<Animal> = Box(Animal())
val
```
0
0