Kotlin 泛型

我来为你全面讲解 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),不能写入。

规则AB 的子类型,那么 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),不能读取。

规则AB 的子类型,那么 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?>

©著作权归作者所有,转载或内容合作请联系作者
【社区内容提示】社区部分内容疑似由AI辅助生成,浏览时请结合常识与多方信息审慎甄别。
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

相关阅读更多精彩内容

友情链接更多精彩内容