Kotlin 的 Generics

Kotlin 的 Generics(泛型)本质上是参数化类型(parametric polymorphism),用于在类型层面抽象算法或数据结构,使其具备类型安全(type safety)与复用性(reusability)。


一、基本语法

1️⃣ 泛型类

kotlin 复制代码
class Box<T>(val value: T)
  • T 是类型参数(type parameter)
  • 使用时指定具体类型:
kotlin 复制代码
val intBox = Box<Int>(10)
val strBox = Box<String>("hello")

2️⃣ 泛型函数

kotlin 复制代码
fun <T> printValue(value: T) {
    println(value)
}
  • <T> 写在函数名前
  • 调用时通常可以类型推断:
kotlin 复制代码
printValue(123)     // T = Int
printValue("abc")   // T = String

二、类型约束(Upper Bound)

泛型默认是 T : Any?

如果你想限制类型:

kotlin 复制代码
fun <T : Number> sum(a: T, b: T): Double {
    return a.toDouble() + b.toDouble()
}

这里表示:

复制代码
T 必须是 Number 或其子类

多重约束

kotlin 复制代码
fun <T> example(value: T)
where T : Number, T : Comparable<T> {
}

表示:

  • 必须是 Number
  • 必须可比较

三、变型(Variance)------核心重点

这是 Kotlin 泛型最关键的部分。

1️⃣ 不变(Invariant)

默认情况下:

kotlin 复制代码
class Box<T>

那么:

复制代码
Box<Int> ≠ Box<Number>

即使 Int 是 Number 的子类,也不能赋值。


2️⃣ 协变(Covariant)------ out

kotlin 复制代码
class Box<out T>(val value: T)

含义:

复制代码
Box<Int> 是 Box<Number> 的子类型

原则:

只读生产者(Producer)用 out

例子:

kotlin 复制代码
val intBox: Box<Int> = Box(10)
val numberBox: Box<Number> = intBox   // 合法

为什么安全?

因为它只能"输出"T,不能接收T。


协变的限制

kotlin 复制代码
class Box<out T>(val value: T) {
    fun setValue(value: T) { }  // ❌ 错误
}

因为如果允许写入,就可能破坏类型安全。


3️⃣ 逆变(Contravariant)------ in

kotlin 复制代码
interface Consumer<in T> {
    fun consume(item: T)
}

含义:

复制代码
Consumer<Number> 是 Consumer<Int> 的子类型

原则:

只写消费者(Consumer)用 in


4️⃣ 记忆口诀

Producer out

Consumer in

简称 PECS原则(和Java一样)


四、使用点变型(use-site variance)

Kotlin 支持:

kotlin 复制代码
fun copy(from: Array<out Any>, to: Array<Any>) { }

这里 out 是在使用时声明的。


五、星投影(Star Projection)

kotlin 复制代码
val list: List<*> = listOf(1, 2, 3)

等价于:

复制代码
List<out Any?>

你可以读,但读出来类型是 Any?

不能写入。


六、类型擦除(Type Erasure)

和 Java 一样:

泛型在运行时会被擦除

kotlin 复制代码
if (value is List<Int>) { }   // ❌ 编译错误

因为运行时不知道泛型参数。


七、reified ------ 解决类型擦除

Kotlin 的重大优势。

kotlin 复制代码
inline fun <reified T> isType(value: Any): Boolean {
    return value is T
}

必须:

  • inline
  • reified

使用:

kotlin 复制代码
isType<String>("abc")

这在 Java 是做不到的。


八、泛型 + 扩展函数

例如你之前问过:

kotlin 复制代码
fun <T> List<T>.second(): T {
    return this[1]
}

T 来自调用对象。


九、泛型与 sealed / enum 区别

你之前问过 sealed interface。

泛型是:

类型抽象

sealed 是:

类型层级封闭

两个解决的问题不同。


十、常见坑

1️⃣ MutableList 是不协变的

kotlin 复制代码
val ints: MutableList<Int> = mutableListOf(1,2)
val numbers: MutableList<Number> = ints   // ❌

因为可以写入。


2️⃣ List 是协变的

kotlin 复制代码
val ints: List<Int> = listOf(1,2)
val numbers: List<Number> = ints   // ✅

因为它是:

kotlin 复制代码
interface List<out E>

十一、本质理解

泛型本质是:

复制代码
在类型系统层面对算法进行抽象

而变型本质是:

复制代码
控制子类型关系在泛型中的传播方式

Kotlin 泛型 =

  • 编译期类型安全
  • 支持协变/逆变
  • 支持 reified
  • 运行时类型擦除
相关推荐
小强闯江湖6 小时前
不只是让 AI 写代码:ViewCompose 把 Android UI 做成了可编译、可渲染的闭环
android·人工智能·kotlin
bytebitx1 天前
MacOS 编译腾讯 Mars XLog 及使用(已16KB对齐)
kotlin·mac·apk
alexhilton2 天前
让架构边界变成可执行的测试
android·kotlin·android jetpack
数据治理自习室2 天前
AI 应用评测体系(GraphRAG)
android·大数据·人工智能·kotlin
名剑走天下3 天前
Android 面试题大全
android·kotlin
chjif4 天前
Android 设备管控开发实战:自定义 Launcher 如何只显示指定 App?
android·kotlin
非凡ghost4 天前
用 Kotlin 和 Jetpack Compose 重写的安卓视频播放器,原生体验有多流畅?
android·java·kotlin·音视频·软件需求
名剑走天下4 天前
android 面试题
kotlin
蜡台4 天前
Jetpack Compose 稳定性、重组优化(Stable / @NonRestartableComposable)
android·kotlin·compose·jepack
JMchen1235 天前
Jetpack 内核实战(十):实战(下)——编辑页、全局设置与内存泄漏排查
kotlin·内存泄漏·android 实战·datastore 设置·jetpack 项目优化·编辑页开发