Kotlin的语法入门-泛型

前言

在 Kotlin 中,泛型是一种强大的特性,它允许你编写可复用的代码,这些代码可以处理不同类型的数据,而不需要为每种数据类型都编写特定的实现。

泛型在类、接口、函数等方面都有广泛应用,下面将详细介绍 Kotlin 中泛型的相关内容。

泛型类和泛型接口

泛型类和泛型接口在定义时会使用类型参数,这些类型参数在创建类的实例或实现接口时会被具体的类型所替代。

class

1
2
3
4
5
class ResultVo<T> {
var code: Int = 0
var msg: String = ""
var obj: T? = null
}

data class

1
2
3
4
5
data class ResultVo<out T>(
val code: Int,
val msg: String,
val obj: T?
)

泛型函数

泛型函数允许你在函数级别使用类型参数,使得函数可以处理不同类型的数据。

示例代码

1
2
3
4
5
6
7
8
fun <T> printItem(item: T) {
println(item)
}

fun main() {
printItem(10)
printItem("Hello")
}

代码解释

  • printItem<T> 是一个泛型函数,<T> 表示该函数使用了一个类型参数 T。在调用 printItem 函数时,Kotlin 会根据传入的参数自动推断 T 的具体类型。

泛型约束

泛型约束允许你限制类型参数必须是某个特定类型或其子类型,这样可以在泛型代码中使用特定类型的方法和属性。

示例代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
// 定义一个接口
interface Printable {
fun printInfo()
}

// 实现接口
class Person(val name: String) : Printable {
override fun printInfo() {
println("Person: $name")
}
}

// 泛型函数,使用泛型约束
fun <T : Printable> printAll(items: List<T>) {
for (item in items) {
item.printInfo()
}
}

fun main() {
val people = listOf(Person("Alice"), Person("Bob"))
printAll(people)
}

代码解释

  • Printable 是一个接口,Person 类实现了该接口。
  • printAll<T : Printable> 是一个泛型函数,使用了泛型约束 T : Printable,表示类型参数 T 必须是 Printable 接口的实现类。在函数内部,可以调用 T 类型对象的 printInfo 方法。

变型(协变、逆变和不变)

Kotlin 中的变型用于处理泛型类型之间的子类型关系,分为协变、逆变和不变三种情况。

协变(out

协变允许泛型类型作为生产者,只能从中读取数据,不能写入数据。

示例代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
// 协变泛型类
interface Producer<out T> {
fun produce(): T
}

class StringProducer : Producer<String> {
override fun produce(): String = "Hello"
}

fun main() {
val stringProducer: Producer<String> = StringProducer()
val anyProducer: Producer<Any> = stringProducer
println(anyProducer.produce())
}

代码解释

  • Producer<out T> 是一个协变泛型接口,使用 out 关键字表示协变。StringProducer 类实现了 Producer<String> 接口。
  • 由于协变的特性,Producer<String> 类型的对象可以赋值给 Producer<Any> 类型的变量。

逆变(in

逆变允许泛型类型作为消费者,只能向其中写入数据,不能读取数据。

示例代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
// 逆变泛型类
interface Consumer<in T> {
fun consume(item: T)
}

class AnyConsumer : Consumer<Any> {
override fun consume(item: Any) {
println("Consumed: $item")
}
}

fun main() {
val anyConsumer: Consumer<Any> = AnyConsumer()
val stringConsumer: Consumer<String> = anyConsumer
stringConsumer.consume("Hello")
}

代码解释

  • Consumer<in T> 是一个逆变泛型接口,使用 in 关键字表示逆变。AnyConsumer 类实现了 Consumer<Any> 接口。
  • 由于逆变的特性,Consumer<Any> 类型的对象可以赋值给 Consumer<String> 类型的变量。

不变

如果泛型类型既不是协变也不是逆变,则是不变的,即泛型类型之间不存在子类型关系。

星投影(*

星投影用于在不知道具体类型参数时使用泛型类型。

示例代码

1
2
3
4
5
6
7
8
9
10
11
12
13
fun printElements(list: List<*>) {
for (element in list) {
println(element)
}
}

fun main() {
val intList = listOf(1, 2, 3)
val stringList = listOf("a", "b", "c")

printElements(intList)
printElements(stringList)
}

代码解释

  • List<*> 表示一个泛型列表,其中的元素类型未知。printElements 函数可以接受任何类型的列表作为参数。

通过以上介绍,你可以了解到 Kotlin 中泛型的基本概念和使用方法,包括泛型类、泛型接口、泛型函数、泛型约束、变型和星投影等。泛型可以提高代码的复用性和类型安全性,是 Kotlin 中非常重要的特性之一。