文章目录[x]
- 1:泛型
- 2:泛型擦除
- 3:泛型擦除的弊端与解决方法
- 3.1:解决方法一:使用Class参数
- 3.2:解决方法二:使用reified
泛型
在将泛型添加到Java版本5之前,集合中不会存在类型:
List list = new ArrayList();
list.add("str");
list.add(4);//不会出错
//会抛出异常
String str = (String)list.get(1);
当从集合中取出元素的时候都要执行显式的转换,但是我们无法防范无效的强制转换,
为了解决这个问题,在Java 5中添加了泛型。
使用泛型,可以为集合定义特定类型,就不能往集合中添加其他类型的数据。
泛型擦除
泛型提供类型安全性,并帮助你避免显式的类型转换。但是由于Java 5之前没有类型信息,Java编译器为了保证编译之后的字节码和之前Java版本的字节码相同,在编译时会替换所有类型信息,即类型擦除。类型擦除可通过向编译器提供类型信息来保证编译时的类型安全。当在通用函数中使用时,类型擦除可能会受到限制。
泛型擦除的弊端与解决方法
泛型会限制在需要访问通用函数中的类型信息,编译器会告诉你该信息不存在,在下面的代码中,编译器会提示缺少类型信息:
fun <T> printType(){
print(T::class.java)
}
解决方法一:使用Class参数
fun <T> printType(classType:Class<T>){
print(classType::class.java)
}
可以添加Class作为参数进行打印,这样意义并不大,因为在调用之前就可以拿到Class。
解决方法二:使用reified
在使用使用reified后,就可以访问类型的信息:
inline fun <reified T>printType(){
print(T::class.java)
}
fun test(){
printType<String>()
}
为什么要使用内联函数呢?因为如果一个函数使用inline,Kotlin编译器将会把该函数体中的代码拷贝到调用处,这样的优点之一是编译器可以自由的在复制函数主体时对其进行修改,由此reified使用了内联函数。
如上代码在编译成Java后的代码如下:
public final class TestKt {
// $FF: synthetic method
public static final void printType() {
int $i$f$printType = 0;
Intrinsics.reifiedOperationMarker(4, "T");
Class var1 = Object.class;
boolean var2 = false;
System.out.print(var1);
}
public static final void test() {
int $i$f$printType = false;
Class var1 = String.class;
boolean var2 = false;
System.out.print(var1);
}
}
可以看出,Kotlin编译器在对reified函数进行复制拷贝时,会将通用函数体中的类型写成为调用处的类型,这样,就可以有效的避免类型擦除,并且不会带来任何性能上的损失。