
1. Kotlin泛型基础概念回顾在Kotlin中泛型Generics是一种强大的类型系统特性它允许我们在定义类、接口和方法时使用类型参数。这种机制的主要目的是提高代码的复用性和类型安全性。与Java泛型相比Kotlin的泛型系统更加灵活且表达力更强特别是在处理类型变异variance方面。泛型的基本语法是在类名或函数名后使用尖括号声明类型参数。例如class BoxT(t: T) { var value t }这里T就是类型参数在创建实例时可以指定具体类型val box: BoxInt BoxInt(1)Kotlin编译器会在编译时进行类型检查确保类型安全。这意味着如果你声明了一个BoxInt就不能把String类型的值放进去这可以避免运行时出现ClassCastException。提示Kotlin的类型推断非常强大在大多数情况下可以省略类型参数编译器会根据上下文自动推断。例如上面的例子可以简写为val box Box(1)。泛型在Kotlin集合框架中应用广泛。例如ListString表示一个只能包含字符串的列表MapInt, String表示键为整型、值为字符串的映射。这种类型安全保证可以让我们在编译时就发现潜在的类型错误而不是等到运行时。2. 类型变异in与out关键字解析2.1 类型变异的基本概念类型变异指的是泛型类型参数之间的子类型关系如何影响泛型类本身的子类型关系。Kotlin通过in和out关键字提供了声明处变异declaration-site variance的能力这是比Java使用通配符? extends和? super更优雅的解决方案。在Kotlin中默认情况下泛型是不变的invariant。这意味着即使Dog是Animal的子类BoxDog既不是BoxAnimal的子类也不是其父类。这种设计虽然安全但有时过于严格。2.2 out关键字协变out关键字表示类型参数是协变的covariant。这意味着如果A是B的子类型那么ProducerA就是ProducerB的子类型。协变类型参数只能出现在输出位置作为返回值类型不能出现在输入位置作为参数类型。interface Producerout T { fun produce(): T // fun consume(item: T) // 错误T出现在in位置 } fun useProducer() { val dogProducer: ProducerDog ... val animalProducer: ProducerAnimal dogProducer // 合法因为T是out }Kotlin标准库中的List接口就是一个协变的好例子interface Listout E : CollectionE { // 只有返回E的方法没有接收E作为参数的方法 }2.3 in关键字逆变in关键字表示类型参数是逆变的contravariant。这意味着如果A是B的子类型那么ConsumerB就是ConsumerA的子类型。逆变类型参数只能出现在输入位置作为参数类型不能出现在输出位置作为返回值类型。interface Consumerin T { fun consume(item: T) // fun produce(): T // 错误T出现在out位置 } fun useConsumer() { val animalConsumer: ConsumerAnimal ... val dogConsumer: ConsumerDog animalConsumer // 合法因为T是in }Comparable接口是逆变的典型例子interface Comparablein T { operator fun compareTo(other: T): Int }2.4 类型变异的使用场景在实际开发中理解何时使用协变或逆变非常重要协变(out)适用于生产者场景即主要从对象中获取值的场景。例如数据读取接口不可变集合工厂模式逆变(in)适用于消费者场景即主要向对象中传入值的场景。例如数据写入接口事件处理器比较器注意错误地使用变异注解可能导致编译错误或运行时异常。一个简单的记忆方法是PECS原则Producer Extends, Consumer Super在Kotlin中对应Producer out, Consumer in。3. where关键字与泛型约束3.1 泛型约束的基本概念Kotlin允许我们对泛型类型参数施加约束限制可以替代类型参数的具体类型。最常见的约束是上界(upper bound)默认上界是Any?。fun T : ComparableT sort(list: ListT) { ... }这里T : ComparableT表示类型参数T必须是ComparableT的子类型。这样在函数体内就可以调用T的compareTo方法。3.2 where关键字的使用当我们需要对类型参数施加多个约束时可以使用where子句。where子句写在函数或类声明的后面可以指定多个约束条件。fun T copyWhenGreater(list: ListT, threshold: T): ListString where T : CharSequence, T : ComparableT { return list.filter { it threshold }.map { it.toString() } }这个例子中类型参数T必须同时满足是CharSequence的子类型是ComparableT的子类型3.3 多个类型参数的约束where子句也可以用于约束多个类型参数之间的关系interface Entity { val id: String } class DaoT, U where T : Entity, U : Entity, T : ComparableT { fun save(entity: T): U { ... } }3.4 实际应用场景泛型约束在实际开发中有多种应用确保类型具有特定能力fun T ensureTrailingSlash(path: T): T where T : CharSequence, T : Appendable { return if (path.endsWith(/)) path else path.append(/) as T }多接口要求的场景interface JsonConvertible { fun toJson(): String } interface XmlConvertible { fun toXml(): String } fun T logSerialized(obj: T) where T : JsonConvertible, T : XmlConvertible { println(obj.toJson()) println(obj.toXml()) }类型参数之间的关系约束class RelationA, B where A : Entity, B : Entity, A : ComparableA, B : ComparableB { fun compare(a: A, b: B): Boolean { ... } }提示虽然泛型约束提供了强大的类型安全保证但过度使用可能会使代码变得复杂。建议只在确实需要多个约束时才使用where子句对于单一约束使用:语法更简洁。4. 泛型在Kotlin中的高级应用4.1 星号投影(Star Projections)当对泛型类型参数的信息不感兴趣时可以使用星号投影。它类似于Java的原始类型但更安全。fun printSize(list: List*) { println(list.size) }对于声明为out的类型参数星号投影等同于out Any?interface Boxout T val box: Box* ... // 等同于Boxout Any?对于声明为in的类型参数星号投影等同于in Nothinginterface Sinkin T val sink: Sink* ... // 等同于Sinkin Nothing4.2 具体化类型参数(reified)Kotlin通过reified关键字支持在运行时保留泛型类型信息这在内联函数中特别有用inline fun reified T parseJson(json: String): T { return Json.decodeFromString(json) } val user parseJsonUser({name:John})4.3 泛型与扩展函数泛型可以与扩展函数结合提供更灵活的APIfun T ListT.secondOrNull(): T? if (size 2) this[1] else null fun T : ComparableT ListT.sortedDescending(): ListT { return sortedWith(reverseOrder()) }4.4 泛型委托Kotlin的标准库提供了几个有用的泛型委托class PreferencesT(private val key: String, private val defaultValue: T) { private val preferences: SharedPreferences ... var value: T by object : ReadWritePropertyAny?, T { override fun getValue(thisRef: Any?, property: KProperty*): T { return when (defaultValue) { is Int - preferences.getInt(key, defaultValue) is String - preferences.getString(key, defaultValue) // 其他类型处理 else - throw IllegalArgumentException() } as T } override fun setValue(thisRef: Any?, property: KProperty*, value: T) { when (value) { is Int - preferences.edit().putInt(key, value).apply() is String - preferences.edit().putString(key, value).apply() // 其他类型处理 } } } }4.5 泛型与DSL构建泛型可以用于构建类型安全的DSLclass TableT { private val columns mutableListOfColumnT, *() fun V column(name: String, selector: T.() - V): ColumnT, V { return Column(name, selector).also { columns.add(it) } } } class ColumnT, V(val name: String, val selector: T.() - V) fun T table(init: TableT.() - Unit): TableT { return TableT().apply(init) } // 使用示例 data class Person(val name: String, val age: Int) val personTable tablePerson { column(Name) { name } column(Age) { age } }5. 泛型使用中的常见问题与解决方案5.1 类型擦除与运行时类型检查Kotlin和Java一样在运行时会对泛型类型参数进行擦除。这意味着在运行时无法直接检查泛型类型fun T checkType(obj: Any) { if (obj is ListString) { // 警告无法检查擦除的类型 // ... } }解决方案是使用星号投影或reified类型参数inline fun reified T checkType(obj: Any) { if (obj is T) { // 可以检查因为T是具体化的 // ... } }5.2 泛型数组创建问题在Kotlin中创建泛型数组比较复杂因为数组在JVM中是协变的但泛型是不变的fun T createArray(size: Int): ArrayT { return arrayOfNullsAny(size) as ArrayT // 不安全的转换 }更安全的做法是使用Array构造函数并提供工厂函数fun T createArray(size: Int, init: (Int) - T): ArrayT { return Array(size, init) }5.3 泛型与可变参数当泛型遇到可变参数时需要注意数组创建的问题fun T printAll(vararg items: T) { items.forEach { println(it) } } printAll(a, b, c) // 正常工作 val list listOf(a, b, c) printAll(*list.toTypedArray()) // 需要显式转换为数组5.4 自引用泛型类型定义自引用泛型类型时需要注意递归问题interface ComparableT { fun compareTo(other: T): Int } class MyClass : ComparableMyClass { // 正确 override fun compareTo(other: MyClass) 0 }5.5 泛型与反射通过反射可以部分绕过类型擦除的限制inline fun reified T List*.filterByType(): ListT { return filter { it is T }.map { it as T } } val mixedList listOf(1, 2, 3, 4) val numbers mixedList.filterByTypeInt() // [1, 3]6. Kotlin泛型与Java互操作6.1 Kotlin调用Java泛型代码Kotlin可以无缝调用Java泛型代码但需要注意平台类型和空安全// Java代码 public class BoxT { private T value; public T getValue() { return value; } public void setValue(T value) { this.value value; } } // Kotlin调用 val box BoxString() box.value Kotlin val s: String box.value // 可能抛出NPE因为Java的Nullable注解未被识别6.2 Java调用Kotlin泛型代码Kotlin的out和in变异注解会被编译为Java的通配符// Kotlin代码 class KotlinBoxout T(val value: T) // Java调用 KotlinBox? extends String box new KotlinBox(value); String s box.getValue(); // 可以正常工作6.3 平台类型与泛型当Kotlin调用Java代码时泛型类型会被视为平台类型编译器不会强制空安全检查// Java public ListString getNames() { ... } // Kotlin val names getNames() // 类型是ListString! val first: String names[0] // 可能抛出NPE更安全的做法是显式指定类型val names: ListString getNames() val names: ListString? getNames()6.4 互操作中的类型变异处理Kotlin的JvmWildcard和JvmSuppressWildcards注解可以控制生成的Java字节码中的通配符fun export(item: ListJvmSuppressWildcards String) { // 生成的Java签名会使用ListString而不是List? extends String } fun import(): ListJvmWildcard String { // 生成的Java签名会使用List? extends String而不是ListString return listOf(a, b) }6.5 泛型与Java数组Kotlin数组和Java数组互操作时需要特别注意// Java public void processArray(String[] array) { ... } // Kotlin调用 val kotlinArray arrayOf(a, b) processArray(kotlinArray.toTypedArray()) // 需要显式转换对于原始类型数组有专门的类如IntArray、CharArray等它们对应Java的原始类型数组// Java public void processIntArray(int[] array) { ... } // Kotlin调用 val intArray intArrayOf(1, 2, 3) processIntArray(intArray) // 可以直接传递7. 泛型在实际项目中的应用案例7.1 构建类型安全的API响应包装器sealed class ApiResultout T : Any { data class Successout T : Any(val data: T) : ApiResultT() data class Error(val exception: Throwable) : ApiResultNothing() object Loading : ApiResultNothing() } fun T : Any fetchData( call: () - T, onSuccess: (T) - Unit, onError: (Throwable) - Unit ): ApiResultT { return try { val data call() onSuccess(data) ApiResult.Success(data) } catch (e: Throwable) { onError(e) ApiResult.Error(e) } }7.2 实现通用的数据验证框架interface Validatorin T { fun validate(value: T): ValidationResult } data class ValidationResult(val isValid: Boolean, val message: String? null) class ValidatorBuilderT { private val validators mutableListOfValidatorT() fun addValidator(validator: ValidatorT) apply { validators.add(validator) } fun build(): ValidatorT object : ValidatorT { override fun validate(value: T): ValidationResult { for (validator in validators) { val result validator.validate(value) if (!result.isValid) return result } return ValidationResult(true) } } } // 使用示例 val emailValidator ValidatorBuilderString() .addValidator(object : ValidatorString { override fun validate(value: String) ValidationResult( value.contains(), Invalid email format ) }) .build()7.3 类型安全的SharedPreferences封装class PreferenceDelegateT( private val preferences: SharedPreferences, private val key: String, private val defaultValue: T, private val serializer: (T) - String, private val deserializer: (String) - T ) : ReadWritePropertyAny?, T { override fun getValue(thisRef: Any?, property: KProperty*): T { val stringValue preferences.getString(key, null) return stringValue?.let { deserializer(it) } ?: defaultValue } override fun setValue(thisRef: Any?, property: KProperty*, value: T) { preferences.edit().putString(key, serializer(value)).apply() } } class AppPreferences(preferences: SharedPreferences) { var username by PreferenceDelegate( preferences, username, , serializer { it }, deserializer { it } ) var lastLoginTime by PreferenceDelegate( preferences, last_login, 0L, serializer { it.toString() }, deserializer { it.toLong() } ) }7.4 泛型在依赖注入中的应用interface ServiceLocator { fun T : Any resolve(type: KClassT): T fun T : Any register(type: KClassT, provider: () - T) } class DefaultServiceLocator : ServiceLocator { private val services mutableMapOfKClass*, () - Any() override fun T : Any resolve(type: KClassT): T { return services[type]?.invoke() as? T ?: throw IllegalStateException(Service $type not registered) } override fun T : Any register(type: KClassT, provider: () - T) { services[type] provider } } // 使用示例 val locator DefaultServiceLocator() locator.register(UserRepository::class) { UserRepositoryImpl() } val userRepository locator.resolve(UserRepository::class)7.5 构建类型安全的EventBusclass EventBus { private val subscribers mutableMapOfKClass*, MutableList(Any) - Unit() inline fun reified T : Any subscribe(noinline subscriber: (T) - Unit) { subscribers.getOrPut(T::class) { mutableListOf() }.add { event - subscriber(event as T) } } fun post(event: Any) { subscribers[event::class]?.forEach { it(event) } } } // 使用示例 data class UserLoggedInEvent(val userId: String) data class DataLoadedEventT(val data: T) val bus EventBus() bus.subscribeUserLoggedInEvent { event - println(User ${event.userId} logged in) } bus.subscribeDataLoadedEventString { event - println(Data loaded: ${event.data}) } bus.post(UserLoggedInEvent(123)) bus.post(DataLoadedEvent(listOf(1, 2, 3)))