Kotlin Inline Function 2

Kotlin - Inline Functions 2

Reified Type

runtime-available语义

定义部分内容翻译自官方文档:Semantics, Checks and Restrictions
什么是 runtime-available 类型?

  • Kotlin的objectclasstrait(已被废弃) 也被称为 classifier
  • 类型参数为空或者全部类型参数都被reified关键词修饰的classfier。Nothing 是一种例外情况。
  • 一个classifier有多个类型参数,但是每一个类型参数都是被reified修饰的runtime-available类型或者star-project(*)
  • (没有指classifier,也就可以是函数)拥有一个类型参数T,T被reified关键词修饰

runtime-available的限制性

  • 不是runtime-available的不允许放在is, !is, as, as?等右边操作符的右边。
  • 只有单类型参数的inline function的类型参数能够用reified标记。
  • Kotlin原生的Array类型是唯一一个类型参数能够使用reified来标记的。
  • 只有 runtime-available 的类型能够被当作一个参数传给 reified类型参数
Note: 坦白地说官方这部分文档所表达出来的意思跟实际情况(Kotlin - 1.2.20)是有些出入的,也不知道是文档没有及时更新,还是Kotlin这部分特性后面还需要继续迭代更新。但不影响我们学习 inline function reified

reified作用

inline function reified 的典型使用场景是像反射这样,需要在运行时动态获取类型信息的情况。官方网站给了以下一个例子:

1
2
3
4
5
6
7
8
//声明
inline fun <reified T> TreeNode.findParentOfType(): T? {
var p = parent
while (p != null && p !is T) {
p = p.parent
}
return p as T?
}
1
2
//使用
treeNode.findParentOfType<MyTreeNode>()

上面这使用例子并没有展示reified的真正作用,仅仅是展示了如何声明reified和调用被reified声明的inline functions。各位看官可以试试将reified拿掉,例子照样能正常工作。
真正体现reified作用的使用例子是下面这个:

1
inline fun <reified T> membersOf() = T::class.members

如果不加reified修饰,编译器是会报错的。因为被reified修饰的类型参数在编译期进行内联的时候就会被转为对应class的符号引用。下面这个例子清楚地展示了这个转换:

1
2
3
4
5
6
7
8
//Source code C.Kt
class C {
}
fun a() {
val a = C()
val b = membersOf<String>()
}
inline fun <reified T> membersOf() = T::class.members

compiled之后:

from "C.kt"
1
2
3
4
5
6
7
8
9
10
11
12
13
14
public final class com.maxtropy.viewtest.CKt {
public static final void a();
Code:
0: new #8 // class com/maxtropy/viewtest/C
3: dup
4: invokespecial #11 // Method com/maxtropy/viewtest/C."<init>":()V
7: astore_0
8: nop
9: ldc #13 // class java/lang/String
11: invokestatic #19 // Method kotlin/jvm/internal/Reflection.getOrCreateKotlinClass:(Ljava/lang/Class;)Lkotlin/reflect/KClass;
14: invokeinterface #25, 1 // InterfaceMethod kotlin/reflect/KClass.getMembers:()Ljava/util/Collection;
19: astore_1
20: return
}

第9行字节码指令指示从常量池中加载String类型信息的常量,也就是String的符号引用。这个时候String类型常量处于操作栈栈顶,因此第11行字节码操作指令都是作用在他上面,创建了一个包含String类型信息的KClass类型实例;并在第12行,
通过getMembers方法获取其中的信息。