Kotlin 泛型模块
1. 泛型类与泛型函数
泛型类
kotlin
// 泛型类:类型参数在类名后声明
class Box<T>(val value: T) {
fun get(): T = value
fun put(item: T) { /* 存储 */ }
}
val intBox = Box(42) // 编译器推断 Box<Int>
val stringBox: Box<String> = Box("Hello")
// 多类型参数
class Pair<K, V>(val key: K, val value: V)
val pair = Pair("age", 25) // Pair<String, Int>
泛型函数
kotlin
// 泛型函数:类型参数在 fun 后声明
fun <T> singleton(item: T): List<T> = listOf(item)
val list: List<String> = singleton("only")
// 多类型参数
fun <K, V> toPair(first: K, second: V): Pair<K, V> = Pair(first, second)
toPair("name", 25) // Pair<String, Int>
// 泛型扩展函数
fun <T> List<T>.secondOrNull(): T? {
return if (size >= 2) this[1] else null
}
listOf("a", "b", "c").secondOrNull() // "b"
泛型接口
kotlin
// 泛型接口
interface Repository<T> {
fun getById(id: String): T?
fun getAll(): List<T>
}
class UserRepository : Repository<User> {
override fun getById(id: String): User? = users.find { it.id == id }
override fun getAll(): List<User> = users
}
// 实现时指定具体类型
interface Producer<out T> {
fun produce(): T
}
class StringProducer : Producer<String> {
override fun produce() = "Hello"
}
2. 协变 out
什么是协变
kotlin
// 协变:Producer<Cat> 是 Producer<Animal> 的子类型
// 关键字:out(T 只用于输出,不用于输入)
// 等价于 Java 的 <? extends T>
Animal
/ \
Cat Dog
class Animal
class Cat : Animal()
class Dog : Animal()
// Producer<out T>:T 只能出现在返回值位置(生产)
interface Producer<out T> {
fun produce(): T // ✅ T 作为输出
// fun consume(item: T) // ❌ T 作为输入,编译错误
}
// 协变声明后,以下代码合法:
val catProducer: Producer<Cat> = CatProducer()
val animalProducer: Producer<Animal> = catProducer // ✅ Producer<Cat> 赋值给 Producer<Animal>
为什么要协变
kotlin
// 不使用协变的问题
interface ListOld<T> {
fun get(index: Int): T // T 作为输出
fun add(item: T) // T 作为输入
}
// Java 风格:全部不标记
fun copyAll(source: ListOld<Animal>, target: ListOld<Animal>) {
for (i in 0 until source.size) {
target.add(source.get(i)) // 假设 ListOld<Cat> 传入,add(Cat) 到 Animal 列表,OK
}
}
// 问题:
val cats: ListOld<Cat> = ArrayListCat()
val animals: ListOld<Animal> = ArrayListAnimal()
copyAll(cats, animals) // 编译通过,但实际把 Cat 加入 Animal 列表,有类型安全隐患
// Kotlin 协变解决:
interface Producer<out T> {
fun produce(): T
}
// Producer<Cat> 是 Producer<Animal> 的子类型
// Producer<Animal> 的消费者可以接收任何 Producer<Cat>
fun consumeAll(producers: List<Producer<Animal>>) {
producers.forEach { it.produce() }
}
val catProducers: List<Producer<Cat>> = listOf(CatProducer())
consumeAll(catProducers) // ✅ Producer<Cat> 可赋值给 Producer<Animal>
实际使用场景
kotlin
// Kotlin 标准库中的协变
// ReadOnlyList: T 只出现在返回值
interface List<out T> {
operator fun get(index: Int): T
// fun add(item: T) // 不存在,因为 List 是只读的
}
// kotlin.sequences.Sequence 中的 T 全部是输出
interface Sequence<out T> {
fun iterator(): Iterator<T>
}
// Android ViewGroup.LayoutParams 使用协变
// ViewGroup.LayoutParams 是子类型,MatchParent/FillParent 是具体类型
open class LayoutParams {
var width: Int = WRAP_CONTENT
var height: Int = WRAP_CONTENT
}
class ViewGroup {
interface LayoutParams
}
3. 逆变 in
什么是逆变
kotlin
// 逆变:Consumer<T> 中 T 只用于输入
// 关键字:in(T 只用于输入,不用于输出)
// 等价于 Java 的 <? super T>
// Consumer<Animal> 是 Consumer<Cat> 的子类型(反直觉!)
interface Consumer<in T> {
fun consume(item: T) // T 作为输入
// fun produce(): T // ❌ T 作为输出,编译错误
}
val animalConsumer: Consumer<Animal> = AnimalConsumer()
val catConsumer: Consumer<Cat> = animalConsumer // ✅ Consumer<Animal> 赋值给 Consumer<Cat>
逆变的直觉理解
kotlin
// 逆变为什么反直觉:
// Consumer<Animal> 可以消费 Animal 及其子类(Cat, Dog)
// Consumer<Cat> 只能消费 Cat
// 如果 Consumer<Animal> 是 Consumer<Cat> 的子类型:
// 那么 Consumer<Animal> 可以放入任何接受 Consumer<Cat> 的地方
// Consumer<Animal> 可以消费 Animal
// Consumer<Cat> 只能消费 Cat
// 所以 Consumer<Animal> 比 Consumer<Cat> 更通用(更"大")
// 更通用的类型是更小的子类型?这很反直觉但数学上成立
// 记忆:in = "放进来的数据",Consumer 消费数据,所以用 in
// Consumer<in T> 的语义:可以接受 T 或 T 的父类型
class SolidColorConsumer : Consumer<Color> {
override fun consume(item: Color) {
println("Consuming color: $item")
}
}
val anyConsumer: Consumer<Any> = SolidColorConsumer()
anyConsumer.consume("String as Any") // ✅ "String as Any" 是 Any 的子类,可以传入
// Consumer<String> vs Consumer<Any>:
// Consumer<String> 只能消费 String
// Consumer<Any> 可以消费 Any, String, Int... 所有 Any 的子类型
// 所以 Consumer<Any> 更通用
// Consumer<String> 是 Consumer<Any> 的子类型(逆变)
协变 + 逆变组合
kotlin
// 功能齐全的类型:既可生产又可消费
interface Transformer<in In, out Out> {
fun transform(input: In): Out // In 作为输入(逆变),Out 作为输出(协变)
}
// 使用
val transformer: Transformer<Any, String> = object : Transformer<Any, String> {
override fun transform(input: Any) = input.toString()
}
// 可以赋值给只关心输出的引用
val stringProducer: Transformer<Nothing, String> = transformer
// 或者只关心输入
val anyConsumer: Transformer<Any, Nothing> = transformer
// 实际使用:Retrofit
interface Call<R> {
interface Converter<F, T>
fun <R> convert(decoder: Converter<F, R>): R
}
4. 星投影 *
何时使用
kotlin
// * 投影:类型未知,只知道有某个类型存在
// 等价于 Java 的 <?>
// 当只需要读取(不关心具体类型)时
val list: List<*> = listOf("a", "b", "c")
val first: Any? = list.first() // 返回类型是 Any?
// 当只需要写入(不关心具体类型)时
fun printFirst(list: List<*>) {
// list[0] 是 Any? 类型,不确定
println(list.firstOrNull())
}
// 不能用 * 的场景
class Box<T>(val value: T) {
fun get(): T = value
}
val box: Box<*> = Box(42)
// box.get() // ❌ 编译错误:T 被 * 投影,不知道返回类型
* vs Any?
kotlin
// 星投影:知道是某个具体类型,但类型参数未知
interface Readable<out T> {
fun read(): T
}
val readable: Readable<*> = StringReadable()
// readable.read() // ❌ 编译错误:返回类型未知
// Any?:完全不知道任何类型信息
interface Readable2 {
fun read(): Any?
}
// List<*> vs List<Any?>
val stringList: List<String> = listOf("a", "b")
val starList: List<*> = stringList // ✅ List<String> 赋值给 List<*>
val anyList: List<Any?> = stringList // ❌ List<String> 不能赋值给 List<Any?>
投影类型转换规则
kotlin
// 对于 Generic<out T>:
// Generic<*> 等价于 Generic<? extends Object>
// 对于 Generic<in T>:
// Generic<*> 等价于 Generic<? super Nothing>
// 对于 Generic<T>(不变):
// Generic<*> 等价于 Generic<?>
// 投影后只能安全读取,类型擦除后无法写入
interface Container<T> {
fun get(): T
fun put(item: T)
}
val container: Container<*> = IntContainer()
// container.get() // 返回类型 Any?
// container.put(42) // ❌ 编译错误:不能写入未知类型
5. 泛型约束与上下界
单个上界
kotlin
// 默认为 Any?(可空)
class Box<T>(val value: T)
// 指定上界:T 必须继承或实现 Number
class NumberBox<T : Number>(val value: T) {
fun double(): Double = value.toDouble()
}
NumberBox(42) // ✅ Int 继承 Number
NumberBox(3.14) // ✅ Double 继承 Number
// NumberBox("text") // ❌ String 不继承 Number
// Comparable 的典型使用
fun <T : Comparable<T>> max(a: T, b: T): T {
return if (a >= b) a else b
}
max(1, 2) // ✅ Int 实现 Comparable<Int>
max("a", "b") // ✅ String 实现 Comparable<String>
// max(1, "a") // ❌ 类型不匹配
多个约束
kotlin
// 多个约束用 where 子句
interface Readable
interface Closeable
class DataProcessor<T> where T : Readable, T : Closeable {
fun process(item: T) {
item.read() // ✅ Readable 方法
item.close() // ✅ Closeable 方法
}
}
// ViewModel 的约束示例
abstract class BaseViewModel : ViewModel(), ViewModelStoreOwner
inline fun <reified VM : BaseViewModel> ViewModelFactory<VM>.create(): VM {
return create(VM::class.java)
}
上界通配符
kotlin
// Java 风格(仍可用)
// <? extends T> 等价于 Kotlin <out T>
// <? super T> 等价于 Kotlin <in T>
// Kotlin 泛型没有通配符,直接用 out/in
// 实际使用:
// 读操作 → Consumer<out T> / producers: List<out T>
// 写操作 → Producer<in T> / consumer: Consumer<in T>
// 常见陷阱:Pair<Int, Int> 不是 Pair<Number, Number> 的子类型
val pairInt: Pair<Int, Int> = 1 to 2
// val pairNum: Pair<Number, Number> = pairInt // ❌ 编译错误
// 原因:Pair 定义是 class Pair<out A, out B>
// 但 A 和 B 各是独立的位置,Pair<A1, B1> 不是 Pair<A2, B2> 的子类型
// 解决方案:自定义类型变量
fun <T> copyTo(dst: MutableList<in T>, src: List<T>) {
dst.addAll(src)
}
val numbers: List<Int> = listOf(1, 2, 3)
val anyList: MutableList<Any> = mutableListOf()
copyTo(anyList, numbers) // ✅ Int 是 Any 的子类型,可放入 MutableList<Any>
上下文边界
kotlin
// 上下界组合
class Bounded<T : Comparable<T>>(val value: T) {
fun greaterThan(other: T): Boolean = value > other
}
// 多条件边界
interface Cloneable
interface Serializable
class Container<T>(
val item: T
) where T : Cloneable, T : Serializable {
fun cloneAndSerialize(): ByteArray {
// item 是 Cloneable 和 Serializable
return serialize(item)
}
}
6. 泛型与类型擦除
类型擦除原理
kotlin
// 运行时泛型信息被擦除(类型擦除)
class Box<T>(val item: T)
Box<String>("test") // 运行时类型:Box
Box::class.java.typeParameters[0] // 运行时的类型参数是 Unknown
// 泛型约束在运行时可检查
inline fun <reified T> checkType(value: Any) {
println(value is T) // reified 允许运行时检查
}
checkType<String>("abc") // true
checkType<Int>("abc") // false
// 非 reified 函数无法在运行时检查类型
fun <T> nonReifiedCheck(value: Any) {
// if (value is T) {} // ❌ 编译错误:无法检查 T
}
// inline + reified 实现运行时类型获取
inline fun <reified T> randomOrNull(): T? {
return if (Math.random() > 0.5) T::class.javaObjectType.newInstance() else null
}
泛型与非特化调用
kotlin
// 泛型擦除后的行为
class GenericClass<T> {
fun method(value: T) {
// 编译后:method(Object value)
}
}
// 多态调用时的问题
open class Base<T> {
open fun execute(item: T) {}
}
class Derived : Base<String>() {
override fun execute(item: String) {}
}
// 运行时:Base.execute(Object) 被调用
// Java 中使用泛型可能会遇到桥接方法问题
7. 综合实战
完整的泛型 Repository
kotlin
interface Repository<out T> {
fun getAll(): List<T>
}
interface MutableRepository<T> {
fun getAll(): List<T>
fun save(item: T)
fun delete(item: T)
}
// 协变 Repository:只读
class UserRepository : Repository<User> {
private val users = mutableListOf(
User("1", "Alice"),
User("2", "Bob")
)
override fun getAll(): List<User> = users.toList()
}
val repo: Repository<User> = UserRepository()
val allUsers: List<User> = repo.getAll()
// 逆变 MutableRepository:只写
class BatchSaver<T> : MutableRepository<T> {
private val items = mutableListOf<T>()
override fun getAll(): List<T> = items.toList()
override fun save(item: T) {
items.add(item)
}
override fun delete(item: T) {
items.remove(item)
}
}
// Consumer<in T> 允许保存任何类型的 User(User 的子类型)
val saver: MutableRepository<User> = BatchSaver<User>()
val catSaver: MutableRepository<Cat> = saver // ❌ 类型不匹配
// 但 BatchSaver<Any> 可以保存任何类型
val anySaver: BatchSaver<Any> = BatchSaver()
val userSaver: BatchSaver<User> = anySaver // ✅ BatchSaver<Any> 赋值给 BatchSaver<User>
Retrofit + 泛型封装
kotlin
// 泛型 API 响应封装
data class ApiResponse<T>(
val code: Int,
val message: String?,
val data: T?
)
class ApiResult<out T> {
val data: T?
val error: String?
private constructor(data: T?, error: String?) {
this.data = data
this.error = error
}
companion object {
fun <T> success(data: T): ApiResult<T> = ApiResult(data, null)
fun <T> failure(error: String): ApiResult<T> = ApiResult(null, error)
}
}
// Repository 层使用
class UserRemoteDataSource(private val api: ApiService) {
suspend fun getUsers(): ApiResult<List<User>> {
return try {
val response = api.getUsers()
if (response.code == 0) {
ApiResult.success(response.data ?: emptyList())
} else {
ApiResult.failure(response.message ?: "Unknown error")
}
} catch (e: Exception) {
ApiResult.failure(e.message ?: "Network error")
}
}
}
// ViewModel 使用
class UserViewModel(
private val remoteDataSource: UserRemoteDataSource
) : ViewModel() {
private val _uiState = MutableStateFlow<ApiResult<List<User>>>(ApiResult.failure("Not loaded"))
val uiState: StateFlow<ApiResult<List<User>>> = _uiState.asStateFlow()
}
快速对照表
| 概念 | 关键字 | 含义 | 等价 Java |
|---|---|---|---|
| 协变 | out T |
T 只用于输出 | <? extends T> |
| 逆变 | in T |
T 只用于输入 | <? super T> |
| 不变 | T |
T 既输入又输出 | 无通配符 |
| 星投影 | * |
类型参数未知 | <?> |
| 上界约束 | T : Number |
T 必须是 Number 子类 | <T extends Number> |
| 多重约束 | where T : A, T : B |
T 必须同时满足 | <T extends A & B> |
| reified | reified T |
运行时保留类型信息 | 无 |
记忆口诀:
- out = 生产者 = 只能读 = 协变(子类型向更宽泛)
- in = 消费者 = 只能写 = 逆变(子类型向更具体)
- 不变 = 既有读又有写 = 不能协变也不能逆变
- *** = 不知道具体类型 = 只读不安全,只写更安全**
实战选择:
- 集合只读 →
List<out T> - 集合只写 →
MutableList<in T> - Repository 读层 →
Repository<out T> - Repository 写层 →
MutableRepository<T> - 同时读写 →
Transformer<in In, out Out>