我来为你全面讲解 Kotlin 的泛型(Generics),这是 Kotlin 类型系统中非常强大且重要的特性。与 Java 相比,Kotlin 泛型引入了声明处型变(Declaration-site Variance)、类型投影(Type Projection) 和 reified 关键字等独特概念。
一、泛型基础
1. 什么是泛型
泛型允许你在定义类、接口、方法时使用类型参数,在实例化时指定具体类型,提高代码复用性和类型安全性。
// 泛型类
class Box<T>(val content: T) {
fun getContent(): T = content
}
// 泛型函数
fun <T> singletonList(item: T): List<T> = listOf(item)
// 使用
val intBox = Box<Int>(42) // 显式指定类型
val stringBox = Box("Hello") // 类型推断(自动推断为 Box<String>)
val list = singletonList(100) // 推断为 List<Int>
2. 多个类型参数
class Pair<K, V>(val key: K, val value: V) {
override fun toString(): String = "($key, $value)"
}
val pair = Pair<String, Int>("age", 30)
// 类型推断也可以
val pair2 = Pair("name", "Alice") // Pair<String, String>
二、泛型约束(Type Bounds)
限制类型参数必须是某个类型的子类。
1. 上界约束(Upper Bound)
// 约束 T 必须是 Number 的子类
fun <T : Number> sum(a: T, b: T): Double {
return a.toDouble() + b.toDouble()
}
sum(10, 20) // ✅ Int 是 Number 的子类
sum(3.14, 2.71) // ✅ Double 是 Number 的子类
// sum("a", "b") // ❌ 编译错误:String 不是 Number 的子类
2. 多个上界(使用 where 子句)
interface Printable {
fun print()
}
interface Serializable {
fun serialize(): String
}
// T 必须同时是 Number 的子类,并且实现 Printable 和 Serializable
fun <T> saveIfCan(item: T) where T : Number, T : Printable, T : Serializable {
item.print()
println("Saved: ${item.serialize()}")
}
// 具体类
class MyNumber(val value: Int) : Number(), Printable, Serializable {
override fun toDouble() = value.toDouble()
override fun toFloat() = value.toFloat()
override fun toLong() = value.toLong()
override fun toInt() = value
override fun toByte() = value.toByte()
override fun toShort() = value.toShort()
override fun toChar() = value.toChar()
override fun print() = println("Value: $value")
override fun serialize() = "MyNumber($value)"
}
// 使用
saveIfCan(MyNumber(42)) // ✅ 满足所有约束
3. 可空性约束
默认的泛型类型是可空的(T?),如果希望非空,需要显式指定上界。
// 默认 T 是可空的
class Container<T>(val value: T?) // T 本身是可空类型
// 指定非空上界(但 Any 本身可空,Any? 才是非空)
fun <T : Any> nonNullExample(item: T): T {
return item // 保证 T 永远不会是 null
}
// nonNullExample(null) // ❌ 编译错误
nonNullExample("Hello") // ✅
三、型变(Variance)
这是 Kotlin 泛型最核心也最复杂的概念。主要解决类型安全和子类型关系的问题。
1. 协变(Covariance)—— out
定义:Producer<out T> 表示该类是 T 的生产者(只能读取/输出 T),不能写入。
规则:A 是 B 的子类型,那么 Producer<A> 也是 Producer<B> 的子类型。
// 声明处协变(Declaration-site Variance)
interface Producer<out T> {
fun produce(): T // ✅ 可以返回 T
// fun consume(item: T) // ❌ 不能接受 T 作为参数(逆变位置)
}
// 具体实现
class StringProducer : Producer<String> {
override fun produce(): String = "Hello"
}
fun useProducer(producer: Producer<Any>) {
println(producer.produce())
}
fun main() {
val stringProducer: Producer<String> = StringProducer()
useProducer(stringProducer) // ✅ 协变:Producer<String> 可以传给 Producer<Any>
}
常见协变类:List<out E>(不可变列表)
val strings: List<String> = listOf("A", "B", "C")
val anyList: List<Any> = strings // ✅ List 是协变的
2. 逆变(Contravariance)—— in
定义:Consumer<in T> 表示该类是 T 的消费者(只能写入/接收 T),不能读取。
规则:A 是 B 的子类型,那么 Consumer<B> 是 Consumer<A> 的子类型(方向相反)。
// 声明处逆变
interface Consumer<in T> {
fun consume(item: T) // ✅ 可以接受 T
// fun produce(): T // ❌ 不能返回 T(协变位置)
}
class AnyConsumer : Consumer<Any> {
override fun consume(item: Any) {
println("Consumed: $item")
}
}
fun useConsumer(consumer: Consumer<String>) {
consumer.consume("Hello")
}
fun main() {
val anyConsumer: Consumer<Any> = AnyConsumer()
useConsumer(anyConsumer) // ✅ 逆变:Consumer<Any> 可以传给 Consumer<String>
}
常见逆变类:Comparable<in T>
// Comparable<Any> 是 Comparable<String> 的子类型
val anyComparator: Comparator<Any> = Comparator { a, b -> a.hashCode() - b.hashCode() }
val stringComparator: Comparator<String> = anyComparator // ✅ 逆变
3. 不变(Invariant)
默认情况下(不加 in/out),泛型是不变的:
class MutableList<T> // 默认不变
val mutableStrings: MutableList<String> = mutableListOf("A", "B")
// val mutableAny: MutableList<Any> = mutableStrings // ❌ 编译错误
// MutableList<String> 不是 MutableList<Any> 的子类型
4. 型变总结表
| 修饰符 | 作用 | 可读 | 可写 | 子类型关系 |
|---|---|---|---|---|
out T |
协变(生产者) | ✅ | ❌ |
Producer<Sub> → Producer<Super>
|
in T |
逆变(消费者) | ❌ | ✅ |
Consumer<Super> → Consumer<Sub>
|
| 无修饰符 | 不变 | ✅ | ✅ | 无子类型关系 |
四、类型投影(Type Projection)
当使用处(use-site)需要型变,但类定义没有 in/out 时,可以使用类型投影。
1. 星投影(Star Projection)—— *
fun printFirst(list: List<*>) { // 未知类型的列表
val first = list.first()
println(first) // 可以读取,类型为 Any?
// list.add(...) // ❌ 不能写入
}
printFirst(listOf(1, 2, 3)) // ✅
printFirst(listOf("A", "B", "C")) // ✅
星投影规则:
-
Foo<*>等价于Foo<out Any?>(协变投影) -
MutableList<*>等价于MutableList<out Any?>(只能读,不能写)
2. 使用处型变(Use-site Variance)
// 定义一个不变类
class Box<T>(var value: T)
fun copy(from: Box<out Any>, to: Box<in Any>) {
// from 是生产者(只读),to 是消费者(只写)
to.value = from.value
}
fun main() {
val intBox = Box(42)
val anyBox = Box<Any>("初始值")
copy(intBox, anyBox) // ✅ 协变投影:Box<out Any> 接受 Box<Int>
println(anyBox.value) // 输出 42
}
3. 投影对比表
// 场景:定义一个不变的 MutableList<T>
fun demo(list: MutableList<*>) {
// 只能读,类型为 Any?
val item = list.get(0) // ✅
// list.add(42) // ❌ 不能写
}
fun demoOut(list: MutableList<out Number>) {
// 只能读,类型为 Number
val num = list.get(0) // ✅
// list.add(10) // ❌ 不能写
}
fun demoIn(list: MutableList<in Number>) {
// 只能写 Number 及其子类型
list.add(10) // ✅
list.add(3.14) // ✅
// val num = list.get(0) // ⚠️ 可以读,但类型是 Any?
}
五、reified 关键字(具体化类型参数)
Kotlin 的泛型在运行时会被类型擦除(Type Erasure),但使用 inline + reified 可以保留类型信息。
1. 基本用法
// ❌ 普通的泛型函数无法获取类型
fun <T> isString(value: Any): Boolean {
// return value is T // 编译错误:无法检查 T 的类型
return false
}
// ✅ 使用 reified 保留类型信息
inline fun <reified T> isA(value: Any): Boolean {
return value is T // 可以检查类型
}
fun main() {
println(isA<String>("Hello")) // true
println(isA<Int>("Hello")) // false
println(isA<Int>(42)) // true
}
2. 常见应用场景
场景 1:类型安全的转换
inline fun <reified T> safeCast(obj: Any): T? {
return if (obj is T) obj else null
}
val result: Int? = safeCast<Int>("123") // null
val result2: Int? = safeCast<Int>(456) // 456
场景 2:获取泛型类的 KClass
inline fun <reified T> getClassName(): String {
return T::class.simpleName ?: "Unknown"
}
println(getClassName<String>()) // "String"
println(getClassName<List<Int>>()) // "List"
场景 3:Android Intent 传递(实际应用)
inline fun <reified T : Activity> Context.startActivity() {
val intent = Intent(this, T::class.java)
startActivity(intent)
}
// 使用
startActivity<MainActivity>() // 无需传入 Class 参数
场景 4:Gson 反序列化
inline fun <reified T> Gson.fromJson(json: String): T {
return fromJson(json, T::class.java)
}
// 使用
val data = gson.fromJson<User>(jsonString)
3. reified 的限制
- 必须与
inline一起使用 - 不能用于非
inline函数 - 不能用于类/接口的属性(只能用于函数)
六、泛型约束进阶
1. 递归类型约束(F-Bounded Polymorphism)
// 要求子类型必须可比较
interface Comparable<T> {
fun compareTo(other: T): Int
}
// 递归约束:T 必须实现 Comparable<T>
fun <T : Comparable<T>> max(a: T, b: T): T {
return if (a > b) a else b // 这里 > 是 compareTo 的语法糖
}
// 使用
println(max(10, 20)) // 20
println(max("A", "B")) // "B"
2. 类型推断与泛型
// 普通函数
fun <T> identity(value: T): T = value
// 显式指定类型
val result: String = identity("Hello")
val result2 = identity<String>("World")
// 类型推断失败时
val list = identity(listOf(1, 2, 3)) // 推断为 List<Int>
// 但有时需要指定
val empty: List<Int> = identity(emptyList()) // 显式声明
val empty2 = identity<Int>(emptyList()) // 或显式类型参数
七、泛型与集合
1. 协变集合(只读)
val immutableList: List<Int> = listOf(1, 2, 3)
// immutableList.add(4) // ❌ 没有 add 方法(只读)
// 协变允许向上转型
val anyList: List<Any> = immutableList // ✅
2. 不变集合(可变)
val mutableList: MutableList<Int> = mutableListOf(1, 2, 3)
mutableList.add(4) // ✅
// val anyMutableList: MutableList<Any> = mutableList // ❌ 不变
val anyMutableList: MutableList<out Any> = mutableList // ✅ 投影只读
3. 类型安全的集合操作
// 协变:只读集合
fun copyToDestination(from: List<out Any>, to: MutableList<in Any>) {
to.addAll(from)
}
fun main() {
val numbers = listOf(1, 2, 3)
val destinations = mutableListOf<Any>()
copyToDestination(numbers, destinations)
println(destinations) // [1, 2, 3]
}
八、实战综合示例
示例 1:类型安全的 Builder
class Builder<T> {
private var result: T? = null
private var factory: (() -> T)? = null
fun withFactory(factory: () -> T): Builder<T> {
this.factory = factory
return this
}
fun build(): T {
return factory?.invoke() ?: throw IllegalStateException("Factory not set")
}
}
inline fun <reified T> build(block: Builder<T>.() -> Unit): T {
return Builder<T>().apply(block).build()
}
// 使用
data class Person(val name: String, val age: Int)
val person = build<Person> {
withFactory { Person("Alice", 30) }
}
println(person) // Person(name=Alice, age=30)
示例 2:泛型仓储模式
// 实体基类
open class Entity(val id: Int)
// 仓储接口(协变)
interface Repository<out T : Entity> {
fun getById(id: Int): T?
fun getAll(): List<T>
}
// 具体实现(逆变,可以消费父类型)
class InMemoryRepository<T : Entity>(
private val data: MutableMap<Int, T> = mutableMapOf()
) : Repository<T> {
override fun getById(id: Int): T? = data[id]
override fun getAll(): List<T> = data.values.toList()
fun save(item: T) {
data[item.id] = item
}
}
// 使用
data class User(override val id: Int, val name: String) : Entity(id)
fun processUsers(repository: Repository<Any>) {
val all = repository.getAll()
println("共 ${all.size} 个用户")
}
fun main() {
val userRepo = InMemoryRepository<User>()
userRepo.save(User(1, "Alice"))
userRepo.save(User(2, "Bob"))
processUsers(userRepo) // ✅ 协变:Repository<User> -> Repository<Any>
}
示例 3:类型安全的回调
// 使用 reified 实现类型安全的事件分发
class EventBus {
private val handlers = mutableMapOf<Class<*>, MutableList<(Any) -> Unit>>()
inline fun <reified T> subscribe(crossinline handler: (T) -> Unit) {
val clazz = T::class.java
handlers.getOrPut(clazz) { mutableListOf() }
.add { event -> handler(event as T) }
}
inline fun <reified T> post(event: T) {
val clazz = T::class.java
handlers[clazz]?.forEach { it.invoke(event) }
}
}
// 使用
data class UserEvent(val userId: Int)
data class RefreshEvent(val timestamp: Long)
fun main() {
val bus = EventBus()
bus.subscribe<UserEvent> { event ->
println("用户事件: ${event.userId}")
}
bus.subscribe<RefreshEvent> { event ->
println("刷新时间: ${event.timestamp}")
}
bus.post(UserEvent(123)) // 用户事件: 123
bus.post(RefreshEvent(1000)) // 刷新时间: 1000
}
九、性能注意事项
reified 的性能影响
// ❌ 普通泛型函数会有类型擦除
fun <T> regularFunction(value: T) {
// 运行时无法知道 T 的具体类型
}
// ✅ reified 需要 inline,可能增加字节码大小
inline fun <reified T> reifiedFunction(value: T) {
// 但可以保留类型,性能更好(无反射)
}
// 使用 reified 替代反射,性能更优
inline fun <reified T> createInstance(): T {
// 无反射,直接调用构造函数
return T::class.java.getDeclaredConstructor().newInstance()
}
十、关键总结
| 概念 | 关键字/符号 | 用途 |
|---|---|---|
| 泛型类/函数 | <T> |
类型参数化 |
| 上界约束 | <T : SuperType> |
限制类型范围 |
| 多个约束 |
where 子句 |
同时满足多个条件 |
| 协变 | out |
生产者(只读) |
| 逆变 | in |
消费者(只写) |
| 星投影 | * |
未知类型 |
| 具体化类型 |
reified + inline
|
保留运行时类型信息 |
选择指南
// 1. 是否需要生产 T?(只读)
→ 使用 out T(协变)
class Producer<out T> { fun get(): T }
// 2. 是否需要消费 T?(只写)
→ 使用 in T(逆变)
class Consumer<in T> { fun accept(item: T) }
// 3. 既生产又消费?
→ 不变(无 in/out)
class MutableBox<T> { var value: T }
// 4. 需要检查泛型类型?
→ 使用 reified + inline
inline fun <reified T> checkType(obj: Any) = obj is T
// 5. 类型不确定,只读取?
→ 星投影 List<*> 或投影 List<out Any?>