Kotlin泛型抛弃语法上的差异,在一般情况下与Java都是相近的,所以在这里简单介绍一下Kotlin泛型中独有的东西。
- 泛型扩展属性:
val <T> List<T>.penultimate: T
get() = this[size - 2]
>>> println(listof(1, 2, 3, 4).penultimate)
通过上面的例子可以看出,泛型扩展属性的实现相当简单,这里就不做过多的讲解,但是有一点需要注意的是:由于不能在一个类的属性中存储多个不同类型的值,因此普通属性(即类的非扩展属性)不能声明为泛型。
- 非空约束:
class A<T> {
fun doSomething(value: T) {
value?.hashCode()
}
}
从value?.hashCode()
中我们可以得知,参数value
是可空的,所以我们需要对它进行安全调用,也就是说类型T
的默认上界为Any?
,那如果我们想要保证类型T
永远为非空类型
怎么办呢?我们可以对上述代码做以下改造:
class A<T : Any> {
fun doSomething(value: T) {
value.hashCode()
}
}
对,很简单,我们只要将类型T
的默认上界改为Any
即可。
- 实例化类型参数
相信大家都知道,在Java中,泛型类实例的类型参数在运行时是不保留的,也就是说对于如List<Integer>
和List<String>
这样的类型声明在运行时得到的都是同样的List
,我们无法从List
中识别出它里面包含的究竟时Integer
还是String
,这样的现象我们称之为类型擦除。也正是因为此,我们想要用Java实现下面的功能是不可能的:
class TypeCheck<T> {
boolean isMe(Object value) {
return value instanceof T;
}
}
public class Demo {
public static void main(String[] args) {
TypeCheck<String> stringCheck = new TypeCheck<>();
System.out.println(stringCheck.isMe("HELLO WORLD"));
}
}
上面是Java的表现,那么在Kotlin又是如何呢?让我们把上面的例子翻译为Kotlin:
class TypeCheck<T> {
fun isMe(value: Any): Boolean {
return value is T
}
}
fun main(args: Array<String>) {
val stringCheck = TypeCheck<String>()
println(stringCheck.isMe("HELLO WORLD"))
}
如果我们尝试编译上面的代码,我们将得到以下错误:
Error:(3, 25) Kotlin: Cannot check for instance of erased type: T
通过对比,我们发现不论在Java中还是在Kotlin中,泛型类实例的类型参数在运行时都是不保留的(即发生了类型擦除),那这是否意味着在Kotlin中我们也无法实现这种类型检查的功能呢?答案当然是否定的,要想在Kotlin的运行时中引用泛型参数实际的类型实参,唯一的方式是使用内联函数。首先让我们首先看下面的例子:
inline fun <reified T> isMe(value: Any): Boolean {
return value is T
}
fun main(args: Array<String>) {
println(isMe<String>("HELLO WORLD"))
}
编译运行,我们发现这次成功运行了,并且输出为true
。那这究竟是什么鬼呢?在Kotlin:关于内联函数的一些理解中,我们可以得知,编译器会用内联函数的实际代码来替换每一次的函数调用,每次我们调用带实化类型参数(即在内联函数中被reified
关键字修饰的类型参数)的函数时,编译器能够获知该次调用中类型参数的具体类型,因此上述main
函数实际被编译成以下样子:
fun main(args: Array<String>) {
println("HELLO WORLD" is String)
}
很简单对不对?让我们对实例化类型参数
进行一个简单的总结:
- 通过上面的描述,我们可以得知
实例化类型参数
只能作用在内联函数
中,而类、属性、非内联函数中的类型参数是不能通过reified
进行修饰的。 - 我们一般会在类型检查、类型转换、获取::class::java中使用
实例化类型参数
。
上面我们从:泛型扩展属性、非空约束、实例化类型参数三个方面简单介绍了一下Kotlin中泛型相对于Java独有的特性,那么除此之外是否还有别的特性呢?且听下回分解。^ _ ^