Kotlin 泛型
Kotlin 泛型(2025年中文讲解)
泛型(Generics)是 Kotlin 提供的一种强大特性,允许在定义类、接口、函数时使用类型参数,从而提高代码的灵活性和类型安全性。相比 Java,Kotlin 的泛型语法更简洁,支持协变(out)、逆变(in)和类型擦除优化,结合空安全和函数式编程,广泛应用于 Android(Jetpack Compose)、后端(Spring Boot)和 Kotlin Multiplatform(KMP)项目。2025年,Kotlin 2.0(K2 编译器)优化了泛型性能和类型推断,尤其在 KMP 跨平台开发中泛型是共享逻辑的核心。本教程详细讲解 Kotlin 泛型的语法、用法和实践,基于官方文档、CSDN 和知乎,适合初学者和开发者。建议用 Kotlin Playground(https://play.kotlinlang.org/)练习。
一、Kotlin 泛型概览(必知)
- 核心概念:
- 泛型:通过类型参数(
T
、K
等)定义类、接口或函数,延迟类型绑定。 - 类型安全:编译时检查类型,减少运行时错误。
- 协变/逆变:通过
out
和in
关键字控制类型关系。 - 特点:
- 简洁:类型推断减少显式类型声明。
- 空安全:支持可空泛型类型(如
T?
)。 - 灵活:支持泛型类、接口、函数和扩展。
- 2025年趋势:
- Kotlin 2.0 优化泛型类型推断(性能提升约 20%)。
- Android 开发中,泛型用于 ViewModel 和 Compose 数据流。
- KMP 项目中,泛型结合
expect
/actual
实现跨平台类型安全。
二、核心语法与用法(必会)
以下按泛型类、函数、协变/逆变等模块讲解,包含代码示例,直接可运行。
1. 泛型类
- 语法:用
<T>
定义类型参数,T
可代表任意类型。
class Box<T>(val item: T) {
fun getItem(): T = item
}
fun main() {
val intBox = Box<Int>(42)
val stringBox = Box<String>("Kotlin")
println(intBox.getItem()) // 输出:42
println(stringBox.getItem()) // 输出:Kotlin
}
- 说明:
T
是类型参数,创建实例时指定具体类型(如Int
、String
)。- 支持类型推断:
val box = Box("Hello") // 自动推断为 Box<String>
- 空安全:
class NullableBox<T>(val item: T?) {
fun isEmpty(): Boolean = item == null
}
fun main() {
val box = NullableBox<String?>(null)
println(box.isEmpty()) // 输出:true
}
2. 泛型函数
- 语法:在函数名前用
<T>
定义类型参数。
fun <T> printItem(item: T) {
println("Item: $item")
}
fun main() {
printItem(42) // 输出:Item: 42
printItem("Kotlin") // 输出:Item: Kotlin
}
- 类型约束:限制
T
的类型范围,用where
或:
。
fun <T : Number> sum(a: T, b: T): Double {
return a.toDouble() + b.toDouble()
}
fun main() {
println(sum(5, 3)) // 输出:8.0
// println(sum("A", "B")) // 错误:String 非 Number
}
- 多约束:
kotlin fun <T> printIfComparable(item: T) where T : Comparable<T>, T : CharSequence { println(item.compareTo(item)) } fun main() { printIfComparable("Kotlin") // 输出:0 }
3. 泛型接口
- 语法:接口支持泛型,类实现时指定类型。
interface Repository<T> {
fun get(id: Int): T
fun add(item: T)
}
class StringRepository : Repository<String> {
private val items = mutableListOf<String>()
override fun get(id: Int): String = items[id]
override fun add(item: String) = items.add(item)
}
fun main() {
val repo = StringRepository()
repo.add("Kotlin")
println(repo.get(0)) // 输出:Kotlin
}
4. 协变(out)
- 用途:允许父类型引用子类型(如
List<Any>
接受List<String>
)。 - 语法:在类型参数前加
out
,表示只读(生产者)。
interface Producer<out T> {
fun produce(): T
}
class StringProducer : Producer<String> {
override fun produce() = "Kotlin"
}
fun main() {
val producer: Producer<Any> = StringProducer()
println(producer.produce()) // 输出:Kotlin
}
- 说明:
out T
限制T
只用于输出(如返回值),不可用于输入(如参数)。
5. 逆变(in)
- 用途:允许子类型引用父类型(如
Consumer<Any>
接受Consumer<String>
)。 - 语法:用
in
,表示只写(消费者)。
interface Consumer<in T> {
fun consume(item: T)
}
class AnyConsumer : Consumer<Any> {
override fun consume(item: Any) = println("Consumed: $item")
}
fun main() {
val consumer: Consumer<String> = AnyConsumer()
consumer.consume("Kotlin") // 输出:Consumed: Kotlin
}
6. 泛型扩展
- 语法:为泛型类型添加扩展函数。
fun <T> List<T>.secondOrNull(): T? = if (size >= 2) this[1] else null
fun main() {
val numbers = listOf(1, 2, 3)
println(numbers.secondOrNull()) // 输出:2
val empty = emptyList<String>()
println(empty.secondOrNull()) // 输出:null
}
7. 类型擦除
- 说明:Kotlin(基于 JVM)运行时擦除泛型信息,需用
reified
解决。
inline fun <reified T> isInstanceOf(obj: Any): Boolean {
return obj is T
}
fun main() {
println(isInstanceOf<String>("Kotlin")) // 输出:true
println(isInstanceOf<Int>("Kotlin")) // 输出:false
}
- 注意:
reified
需配合inline
函数,用于运行时类型检查。
三、实践示例(综合应用)
- 命令行示例(泛型容器):
class Container<T>(private val item: T) {
fun getItem(): T = item
fun <R> transform(transformer: (T) -> R): Container<R> {
return Container(transformer(item))
}
}
fun main() {
val intContainer = Container(42)
println(intContainer.getItem()) // 输出:42
val stringContainer = intContainer.transform { it.toString() }
println(stringContainer.getItem()) // 输出:42(字符串)
}
- Android 示例(泛型 ViewModel):
import android.os.Bundle
import android.widget.Button
import android.widget.TextView
import androidx.appcompat.app.AppCompatActivity
import androidx.lifecycle.ViewModel
import androidx.lifecycle.ViewModelProvider
class GenericViewModel<T>(private val data: T) : ViewModel() {
fun getData(): T = data
}
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
val button: Button = findViewById(R.id.button)
val textView: TextView = findViewById(R.id.textView)
val viewModel = ViewModelProvider(this)[GenericViewModel::class.java] as GenericViewModel<String>
button.setOnClickListener {
textView.text = viewModel.getData()
}
}
}
布局(res/layout/activity_main.xml
):
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
android:gravity="center"
xmlns:android="http://schemas.android.com/apk/res/android">
<TextView
android:id="@+id/textView"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Press button" />
<Button
android:id="@+id/button"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Show Data" />
</LinearLayout>
功能:点击按钮显示 ViewModel 中的泛型数据(需初始化 ViewModel 数据)。
四、注意事项与最佳实践
- 类型约束:
- 用
where
或:
限制类型,避免无效调用:kotlin fun <T : Number> toDouble(value: T): Double = value.toDouble()
- 协变与逆变:
- 用
out
表示生产者(如只读 List),in
表示消费者(如写入函数)。 - 避免复杂协变/逆变嵌套,影响可读性。
- 类型擦除:
- 使用
reified
和inline
解决运行时类型问题。 - 避免在非内联函数中直接访问
T
的类型。
- 空安全:
- 泛型类型支持
T?
:kotlin class Box<T>(val item: T?) { fun isEmpty() = item == null }
- 2025年趋势:
- Jetpack Compose:泛型用于 State 和 ViewModel 数据流。
- KMP:泛型结合
expect
/actual
实现跨平台:kotlin expect interface Repository<T> { fun get(id: Int): T }
- AI 辅助:IntelliJ 的 Codeium 插件可生成泛型类和函数。
五、学习建议
- 练习:用 Kotlin Playground 实践泛型类、函数和协变/逆变,写容器或转换器。
- 资源:
- 官方文档:https://kotlinlang.org/docs/generics.html
- B站:尚硅谷 Kotlin 教程(免费,包含泛型)。
- CSDN:搜索“Kotlin 泛型”。
- 时间:2-3 天掌握泛型基础,1 周熟悉 Android/KMP 应用。
- 实践:开发小型 App(如泛型数据列表、跨平台 Repository)。
六、总结
Kotlin 泛型必知类型参数、协变(out
)、逆变(in
)和类型擦除,必会定义泛型类、函数和扩展,处理空安全。2025年,Kotlin 2.0 提升泛型性能,Android(Compose)和 KMP 项目中泛型确保类型安全和代码复用。相比 Java,Kotlin 泛型更简洁、灵活,适合快速开发。
如果需要具体场景代码(如 Compose 泛型 State 或 KMP 示例)或有问题,告诉我,我可以提供更详细解答!