面试题答案
一键面试实现步骤
- 获取泛型类的类型信息:使用 Kotlin 反射获取
GenericClass<T, U>
实例的类型信息。 - 提取实际类型参数:从获取的类型信息中提取
T
和U
的实际类型。 - 动态创建新实例:根据提取的实际类型参数,使用反射动态创建新的
GenericClass<T, U>
实例。
代码示例
import kotlin.reflect.KClass
import kotlin.reflect.full.createInstance
class GenericClass<T, U>
fun <T, U> createGenericClassInstance(
instance: GenericClass<T, U>
): GenericClass<T, U> {
// 获取实例的类型
val instanceType = instance::class
// 获取泛型参数
val typeArguments = instanceType.typeParameters
// 获取T和U的实际类型
val tType = instanceType.constructedType.arguments[0].type?.classifier as? KClass<T>
val uType = instanceType.constructedType.arguments[1].type?.classifier as? KClass<U>
// 确保类型不为空
if (tType == null || uType == null) {
throw IllegalArgumentException("无法确定类型参数")
}
// 创建T和U的实例(假设它们有默认构造函数)
val tInstance = tType.createInstance()
val uInstance = uType.createInstance()
// 这里只是示例,实际可能需要根据GenericClass的构造函数来初始化
return GenericClass()
}
Kotlin 反射在复杂泛型场景中可能遇到的难点及解决方案
难点1:擦除
- 问题:Java 和 Kotlin 都存在类型擦除问题,在运行时泛型类型信息可能丢失,对于复杂泛型嵌套,可能难以获取完整准确的类型信息。
- 解决方案:使用
reified
关键字(仅在 inline 函数中可用),或者在定义泛型类时通过一些设计模式(如类型令牌 pattern)来保留类型信息。例如,定义GenericClass<T, U>(private val tClass: KClass<T>, private val uClass: KClass<U>)
,这样在实例化时传入具体类型,在反射时就可以获取到这些类型信息。
难点2:复杂嵌套泛型解析
- 问题:当
T
和U
本身是复杂的泛型类型,如List<Map<String, T>>
这种嵌套结构,解析实际类型参数会变得非常复杂。 - 解决方案:递归解析类型参数,通过
KType
的arguments
属性来逐步获取嵌套的泛型类型。例如,对于List<Map<String, T>>
,先获取List
的类型参数,再获取Map
的类型参数,最终获取到T
的实际类型。
难点3:实例化限制
- 问题:反射创建实例要求目标类有默认构造函数,如果
GenericClass
没有默认构造函数,或者构造函数需要参数,创建实例会变得复杂。 - 解决方案:如果构造函数需要参数,可以通过反射获取构造函数,并传入相应参数来创建实例。例如,
tType.constructors.first().call(/* 参数 */)
。如果构造函数参数也是泛型类型,需要按照上述获取泛型类型参数的方式,递归地获取实际类型并创建参数实例。