Better

业精于勤荒于嬉

Idiomatic Kotlin: reified parameters

Better's Avatar 2019-06-22 Kotlin

  1. 1. Kotlin 中的类型擦除
  2. 2. 类型擦除的问题
  3. 3. Reflection
  4. 4. 参考

Java 中的泛型在编译时期,泛型信息被擦除掉了,使用相近的父类或者直接是Object 类替换泛型,这就是类型擦除(Type Erasure)。
类型擦除减少了中间变量来存储泛型信息,进而减少了运行时的性能开销。

Kotlin 中的类型擦除

kotlin 中也使用到了类型擦除来处理泛型

1
fun <T> getCount(list : List<T>) : Int = list.size

编译后的代码,泛型T没有了

1
2
3
4
public static final int getCount(@NotNull List list) {
Intrinsics.checkParameterIsNotNull(list, "list");
return list.size();
}

类型擦除的问题

由于泛型类型被抹除掉了,所以就不能使用泛型的类型信息。

比如不能使用is来判断类型,下面这样是编译不过的:

1
2
3
fun <T> isListOfString(arg : T) : Boolean{
return arg is List<String>
}

只能通过通过的基类信息来判断,比如使用*

1
2
3
fun <T> isList(arg: T): Boolean {
return arg is List<*>
}

比如在使用as?来转换类型的时候,如果基类信息是一样,总是会成功,但是会提示uncheck cast的警告

Reflection

使用 Reflection 可以让程序在运行期间保持泛型信息不被擦除。

比如在inline函数中使用reified关键字可以在函数体中访问泛型参数。

1
2
3
4
5
6
7
8
inline fun <reified T> doSomethingWithType(obj: T) {
val typeName = T::class.java
println(typeName)
}

fun main(args: Array<String>) {
doSomethingWithType(String())
}

查看编译后的代码:

1
2
3
4
5
6
7
8
9
10
11
12
private static final void doSomethingWithType(Object obj) {
Intrinsics.reifiedOperationMarker(4, "T");
Class typeName = Object.class;
System.out.println(typeName);
}

public static final void main(@NotNull String[] args) {
Intrinsics.checkParameterIsNotNull(args, "args");
new String();
Class typeName$iv = String.class;
System.out.println(typeName$iv);
}
  1. inline函数的泛型信息还是被擦除掉了

  2. 调用处,将泛型的信息替换成了具体的类型信息。这个其实是利用了inline函数的功能,inline函数在编译的时候是将函数体类的代码直接替换到调用处,顺便就将泛型信息进行了替换。

参考

Idiomatic Kotlin: reified parameters

This article was last updated on days ago, and the information described in the article may have changed.