當在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文章: