当在Kotlin中使用反射时,你会和两种不一样的反射API打交道。html
java.lang.reflect
中。由于Kotlin类会被编译成普通的Java字节码,Java反射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 | 是不是伴生对象 |
在 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"
}
复制代码
在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
.....
}
复制代码
学习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#call
与KFunctionN#invoke
的区别在于,KFunctionN#invoke
的形参类型和返回值类型是能够肯定的,编译器能够作检查。若是实参类型和数量与invoke
不一致,编译器不会编译经过。而KCallable#call
对全部类型都有效,也意味着须要开发者进行匹配对应的实参类型和数量,不然运行时会获得异常。
每个
KFunctionN
类型都继承了KFunction
并加上一个额外的拥有数量恰好参数的invoke。例如:KFunction2声明了openator fun invoke(p1:P1,p2:P2):R
。KFunctionN
类型属于合成的编译器生成类型,不能在包kotlin.reflect
中找到它们的声明。意味着可使用任意数量参数的函数接口。
KProperty
与Java的Field
不一样,它通称表明相应的Getter
和Setter
,而Java的Field
一般指字段自己。
KProperty
能够表示任何属性,而它的子类KMutableProperty
能够表示一个var
声明的可变变量。
KProperty
的超类型也是KCallable
,意味着当获取该属性时,也可使用KCallable#call
,KCallable#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文章: