Java 中的泛型在编译时期,泛型信息被擦除掉了,使用相近的父类或者直接是Object 类替换泛型,这就是类型擦除(Type Erasure
)。
类型擦除减少了中间变量来存储泛型信息,进而减少了运行时的性能开销。
Kotlin 中的类型擦除
kotlin 中也使用到了类型擦除来处理泛型
1 | fun <T> getCount(list : List<T>) : Int = list.size |
编译后的代码,泛型T
没有了
1 | public static final int getCount(@NotNull List list) { |
类型擦除的问题
由于泛型类型被抹除掉了,所以就不能使用泛型的类型信息。
比如不能使用is
来判断类型,下面这样是编译不过的:
1 | fun <T> isListOfString(arg : T) : Boolean{ |
只能通过通过的基类信息来判断,比如使用*
:
1 | fun <T> isList(arg: T): Boolean { |
比如在使用as?
来转换类型的时候,如果基类信息是一样,总是会成功,但是会提示uncheck cast
的警告
Reflection
使用 Reflection 可以让程序在运行期间保持泛型信息不被擦除。
比如在inline
函数中使用reified
关键字可以在函数体中访问泛型参数。
1 | inline fun <reified T> doSomethingWithType(obj: T) { |
查看编译后的代码:
1 | private static final void doSomethingWithType(Object obj) { |
inline
函数的泛型信息还是被擦除掉了调用处,将泛型的信息替换成了具体的类型信息。这个其实是利用了
inline
函数的功能,inline
函数在编译的时候是将函数体类的代码直接替换到调用处,顺便就将泛型信息进行了替换。