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
  • 运行时类型擦除
相关推荐
耶叶2 小时前
kotlin的修饰符
android·开发语言·kotlin
147API3 小时前
微软 Copilot Cowork 深度解析:用 Kotlin + 147API 手搓一个 AI Agent
kotlin·claude·147api·copilot cowork
橙子199110163 小时前
Java/Kotlin 与并发
java·python·kotlin
147API3 小时前
Claude API 429 限速治理:RPM/ITPM/OTPM + 令牌桶(Kotlin)
java·spring·kotlin·claude
摘星编程3 小时前
AR 眼镜拯救社恐:我用 Kotlin 写了个拜年提词器
kotlin·ar·restful
147API3 小时前
Claude 模型选型:Opus/Sonnet/Haiku + 成本/限速预算(Kotlin)
android·开发语言·kotlin·147api
Kapaseker17 小时前
一杯美式讲完 Sealed Class
android·kotlin
Kapaseker2 天前
详解 Compose background 的重组陷阱
android·kotlin
黄林晴2 天前
Kotlin 2.3.20-RC2 来了!JPA 开发者狂喜,6 大更新一文速览
android·kotlin