Kotlin知识概括(十四) —— 反射

前序

      当在Kotlin中使用反射时,你会和两种不一样的反射API打交道。html

  • 标准的Java反射,定义在包 java.lang.reflect 中。由于Kotlin类会被编译成普通的Java字节码,Java反射API能够完美地支持它们。
  • Kotlin反射API,定义在包kotlin.reflect中。经过Kotlin反射API你能够访问那些在Java世界里不存在的概念,诸如属性和可空类型。Kotlin反射API并非Java反射API的替代方案。同时,Kotlin反射API没有仅局限于Kotlin类,可使用一样的API访问用任何JVM语言写的类。

类引用

最基本的反射功能是获取 Kotlin 类的运行时引用。java

val daqiKotlinClass = daqiKotlin::class
复制代码

      Kotlin的类引用实际是KClass 类型的值。若是想获取Java类引用,能够在KClass 类实例上使用.java属性获取。android

对于Java类引用,可使用.kotlin将其转换为Kotlin类引用bash

val daqiKotlinJavaClass = daqiKotlin::class.java
val daqiKotlinaClass = daqiKotlinJavaClass.kotlin
复制代码
属性或函数 含义
memberProperties 此类及其全部超类中声明的非扩展属性
memberFunctions 此类及其全部超类中声明的非扩展非静态函数
members 此类及其全部超类中声明的非扩展非静态函数和属性,不包括构造函数
constructors 所有构造函数
isFinal 是不是final类
isOpen 是不是open类
isAbstract 是不是抽象类
isSealed 是不是密封类
isData 是不是数据类
isInner 是不是内部类
isCompanion 是不是伴生对象

引入Kotlin反射

      在 Java 平台上,使用反射功能所需的运行时组件做为单独的 JAR 文件(kotlin-reflect.jar)分发。这样作是为了减小不使用反射功能的应用程序所需的运行时库的大小。若是你须要使用反射,请确保该 .jar文件添加到项目的 classpath 中。函数

在Gradle中添加Kotlin反射jar包(具体可看这里):post

dependencies {
    implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk8"
    
    //添加Kotlin反射jar包
    implementation "org.jetbrains.kotlin:kotlin-reflect"
    testImplementation "org.jetbrains.kotlin:kotlin-test"
    testImplementation "org.jetbrains.kotlin:kotlin-test-junit"
}
复制代码

KCallable

      在Kotlin的类引用KClass里,属性和函数都是存放在KCallable集合members中,而构造函数存储在KFunction集合constructors中。KFunction的超接口是KCallable,也就是说KCallable是函数(包括构造函数)和属性的超接口。学习

KCallable中声明了call方法,容许你调用对应的函数或者对应属性的getter:gradle

public actual interface KCallable<out R> : KAnnotatedElement {
    public fun call(vararg args: Any?): R
    .....
}
复制代码

函数KFunction

      学习lambda的时候,学习过方法引用,使用::操做符引用方法,同时会返回一个KFunction对象。ui

      对于KFunction对象,可使用KCallable#call方法来调用被引用的函数。但::daqi同时也是类型KFunctionN的对象(N表明具体的参数数量,与lambda的Function相似),能够经过invoke方法调用该引用函数。(因为该invoke方法是一个openator方法,按照约定可使用()替代调用invoke方法)spa

fun main(){
    val func = ::daqi
    func.invoke("")
    func.call("")
}

fun daqi(name:String){
}
复制代码

      KCallable#callKFunctionN#invoke的区别在于,KFunctionN#invoke的形参类型和返回值类型是能够肯定的,编译器能够作检查。若是实参类型和数量与invoke不一致,编译器不会编译经过。而KCallable#call对全部类型都有效,也意味着须要开发者进行匹配对应的实参类型和数量,不然运行时会获得异常。

每个KFunctionN类型都继承了KFunction并加上一个额外的拥有数量恰好参数的invoke。例如:KFunction2声明了openator fun invoke(p1:P1,p2:P2):RKFunctionN类型属于合成的编译器生成类型,不能在包kotlin.reflect中找到它们的声明。意味着可使用任意数量参数的函数接口。

属性KProperty

      KProperty与Java的Field不一样,它通称表明相应的GetterSetter,而Java的Field一般指字段自己。

KProperty能够表示任何属性,而它的子类KMutableProperty能够表示一个var声明的可变变量。

      KProperty的超类型也是KCallable,意味着当获取该属性时,也可使用KCallable#callKCallable#call会调用该属性的getter。但Kotlin的属性接口提供了一个更好的获取属性值的方式:get方法。

  • KProperty0<out R>提供的是一个无参的get方法,其类型参数表明属性的类型;

      例如顶层属性,直接使用::操做符引用。由于无需接收者,其属性引用的类型为 KProperty0.

val name = "daqi"

fun main(){
    val property = ::name
    property.get()
    property.call()
}
复制代码
  • KProperty1<T, out R>提供一个须要接收者的get方法,其一个类型参数表明接收者的类型,第二个类型参数表明属性的类型;

      例如 成员属性 或 扩展属性 ,须要一个具体的接收者实例,其属性引用的类型为 KProperty1

fun main(){
    val property = daqiKotlin::name
    val daqi = daqiKotlin()
    property.get(daqi)
}

class daqiKotlin{
    val name = ""
}
复制代码

属性引用只能用于定义在外层或类中的属性,而不能用于局部变量。

      KMutableProperty做为KProperty子类,除了提供对应get方法外,还提供了set方法。

fun main(){
    val property = daqiKotlin::name
    val daqi = daqiKotlin()
    property.get(daqi)
    property.set(daqi,"")
    property.call(daqi)
}

class daqiKotlin{
    var name = ""
}
复制代码

KMutableProperty实例的call方法仍然是调用该属性的getter。

最后

      Kotlin基础知识概括系列到本章结束,这一路过来本身也弄清楚了不少概念,Kotlin基础知识更牢固了。同时也深知本身文笔粗旷,日后会一步步改进的,让文章的读者更容易理解文章内容。

最后推荐3篇很不错的Kotlin文章:

参考资料:

android Kotlin系列:

Kotlin知识概括(一) —— 基础语法

Kotlin知识概括(二) —— 让函数更好调用

Kotlin知识概括(三) —— 顶层成员与扩展

Kotlin知识概括(四) —— 接口和类

Kotlin知识概括(五) —— Lambda

Kotlin知识概括(六) —— 类型系统

Kotlin知识概括(七) —— 集合

Kotlin知识概括(八) —— 序列

Kotlin知识概括(九) —— 约定

Kotlin知识概括(十) —— 委托

Kotlin知识概括(十一) —— 高阶函数

Kotlin知识概括(十二) —— 泛型

Kotlin知识概括(十三) —— 注解

Kotlin知识概括(十四) —— 反射

相关文章
相关标签/搜索