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知識概括(十四) —— 反射

相關文章
相關標籤/搜索