Kotlin 泛型基础
泛型可以让我们在代码中声明类型参数,Kotlin 泛型最基本的使用和 Java 一样,可以声明在类上和函数上,用法也都差不多。
- 声明在函数上时,可将类型参数作为参数或返回值的类型,该函数为泛型函数
- 声明在类上时,可以用在任意一处类型声明处,该类为泛型类
class GenericsDemo<T>(t: T) {
val value = t
}
fun <T> invoke(t: T) : T {
return t
}
我们可以在声明了类型参数的类中,声明一个泛型方法,但如果内部方法所声明的类型参数名称和类上所声明的相同,那么会覆盖类上所声明的类型参数。下面的代码不会报错,并会打印 Hello
字符串。
class GenericsDemo<T>() {
fun <T> invoke(t: T) : T {
return t
}
}
val demo = GenericsDemo<Int>()
println(demo.invoke("Hello"))
此外,我们知道在类中可通过重载来定义同名方法,但这在泛型中并不起作用,如果类中拥有以下两个方法,那么将会报错。
class GenericsDemo<T>() {
// 泛型来自类
fun invoke(t: T) : T {
return t
}
// 泛型来自方法本身
fun <S> invoke(s: S) : S {
return s
}
}
上诉代码报错原因是因为两个方法拥有相同的 signature,也就是在 JVM 看来这两个方法的方法名和参数都是一样的,报错信息如下:
Platform declaration clash: The following declarations have the same JVM signature (invoke(Ljava/lang/Object;)Ljava/lang/Object;):
造成上诉_参数类型覆盖_和_重载签名相同_的原因是在编译成 .class 文件后,类型参数会被擦除。
泛型擦除
Java 和 Kotlin 的泛型都是伪泛型,泛型所进行的类型安全检查仅在编译器进行,在进入 JVM 时这些类型参数都会被移除,运行时不会保留和类型参数相关的信息,我们称这种机制为泛型擦除。
由于泛型是在 JDK 1.5 才引入,为了兼容之前的版本,因此采用泛型擦除来移除运行时的类型参数
泛型擦除时,被擦除的类型参数都会被替换成 Object,这也是为什么上述 invoke()
方法的 signature 为 invoke(Ljava/lang/Object;)Ljava/lang/Object;
。
如果该报错来自 IDEA,它可能会提示采用 @JvmName
注解来处理这个问题,这可以改变编译成字节码后该方法的名称。
@JvmName("invoke1")
fun <S> invoke(s: S) : S {
return s