Kotlin知识归纳(十四) —— 反射

前序

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

  • 标准的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 类的运行时引用。

val daqiKotlinClass = daqiKotlin::class

      Kotlin的类引用实际是KClass 类型的值。如果想获取Java类引用,可以在KClass 类实例上使用.java属性获取。

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

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包(具体可看这里):

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:

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

函数KFunction

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

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

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知识归纳(十四) —— 反射

image
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 159,117评论 4 362
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,328评论 1 293
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 108,839评论 0 243
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 44,007评论 0 206
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,384评论 3 287
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,629评论 1 219
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,880评论 2 313
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,593评论 0 198
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,313评论 1 243
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,575评论 2 246
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,066评论 1 260
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,392评论 2 253
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,052评论 3 236
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,082评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,844评论 0 195
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,662评论 2 274
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,575评论 2 270