Kotlin 从入门到进阶 之泛型 模块(七)

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>
相关推荐
代码中介商1 小时前
C++ 异常处理完全指南
开发语言·c++
MATLAB代码顾问1 小时前
粒子群优化算法(PSO)原理与Python高级实现
开发语言·python·算法
yhy66666661 小时前
java内存
java·开发语言
码云社区1 小时前
JAVA同城上门做饭系统家政上门同城服务系统源码小程序+APP+公众号+h5
java·开发语言·小程序
小灰灰搞电子2 小时前
PyQt QtWaitingSpinner详解-为你的 Qt 程序带来丝滑等待动画
开发语言·qt·pyqt
Gofarlic_oms12 小时前
Adams许可排队严重?不想买新许可,闲置回收立即可用
java·大数据·服务器·开发语言·人工智能
张小凡vip2 小时前
python单元测试详解
开发语言·python·单元测试
爱喝水的鱼丶2 小时前
SAP-ABAP:SAP 系统变量 SY-INDEX 学习笔记:从 1 开始的循环计数器
运维·开发语言·数据库·sap·abap
史迪仔01122 小时前
[QML] Qt6/Qt5四大渐变效果实战指南
开发语言·前端·c++·qt