Android Kotlin的Class、反射、泛型

前言

最近在學(xué)習(xí)kotlin的反射的時(shí)候遇到了一些問題,特地記錄一下。

正題

在Java中使用Class很常見的就是,xxx類.class,比如我們?cè)趕tartActivity的時(shí)候startActivity(new Intent(this, OtherActivity.class)); 這里接收的就是CLass<?> cls參數(shù)。
那么在java中獲取Class的方法有哪些呢?

1、Class c = person.getClass(); //對(duì)象獲取
2、Class cc =Person.class;//類獲取

而我們來看看kotlin

//對(duì)象獲取
person.javaClass// javaClass
person::class.java // javaClass
//類獲取
Person::class// kClass
 person.javaClass.kotlin// kClass
(Person::class as Any).javaClass// javaClass
Person::class.java // javaClass

哇,這么多種,他們是不是一樣的,有沒有什么區(qū)別?
log看看他們到底是不是相同的Class

println(person.javaClass == person::class.java) //true
println(person.javaClass == Person::class.java)//true
println(person::class.java == Person::class.java)//true
 //person.javaClass == person::class.java == Person::class.java
println(person.javaClass == Person::class)//false
println(person.javaClass.kotlin == Person::class)//true
println(person::class == Person::class)//true

從log來看,person.javaClass == person::class.java == Person::class.java
三者是相同的。但是person.javaClass == Person::class卻是不同的。為什么呢?

原因是在kotlin中的Class與Java不同,kotlin中有一個(gè)自己的Class叫做KClass,person::classPerson::class都是獲取kotlin的KClass,所以println(person::class == Person::class) 為true。
我們可以從kotlin的KClass獲取到j(luò)ava的Class,person::class.java就是如此,先獲取到kotlin的KClass然后再獲取javaClass。
object/class->kClass->Class
同樣也可以通過java的Class獲取kotlin的KClass,person.javaClass.kotlin就是先獲取javaClass然后再獲取kotlin的KClass
object/class->Class->KClass
那么KClass都有些什么用呢?Find Usages 可以看到


幾乎多數(shù)跟Reflect相關(guān),而用的最多的也是在KClasses里面
KClasses擴(kuò)展了許多跟反射相關(guān)的方法,算的上是kotlin的反射中類主力輸出。

如果要使用kotlin的反射類的話,要加入

  compile "org.jetbrains.kotlin:kotlin-reflect:$kotlin_version"
舉個(gè)例子

我們要把一個(gè)類的所有字段通過反射給打印出來,調(diào)用java的方法來實(shí)現(xiàn)是這樣

 println (Person::class.java.declaredFields.map {
            it.isAccessible = true
            "${it.name}: ${it.get(person)}"
        }.joinToString(","))

我們通過Person::class拿到KClass,然后.java拿到j(luò)ava的Class<?>,再獲取declaredFields,最后通過map,然后把獲取的Field獲取到值打印出來

問題

而使用kotlin,我們還有別的做法

 println (Person::class.memberProperties.map {
            it.isAccessible = true
            "${it.name}: ${it.get(person)}"
        }.joinToString(","))

通過Person::class拿到KClass,直接調(diào)用KClass的memberProperties來拿到KProperty的Collection集合,然后進(jìn)行操作。當(dāng)然,這里KProperty也是kotlin的反射類中的,也類似于Java的Field。
那么既然可以這樣,理論上這樣也應(yīng)該是可以的

//person是對(duì)象不是Person類
 println (person::class.memberProperties.map {
            it.isAccessible = true
            "${it.name}: ${it.get(person)}"
        }.joinToString(","))

這下卻出錯(cuò)了,真的奇怪!

Error:(73, 31) Out-projected type 'KProperty1<out OtherActivity.Person, Any?>' prohibits the use of 'public abstract fun get(receiver: T): R defined in kotlin.reflect.KProperty1'

為什么會(huì)這樣?
查看it發(fā)現(xiàn)

kotlin的property的Person為out逆變的,R只能作為輸出,不能作為get的參數(shù)傳入,所報(bào)錯(cuò)了。
這里的kotlin泛型還是有點(diǎn)小坑的需要你踩一踩,網(wǎng)上有一篇文章解釋說明了一番

那么怎么改呢?
三種辦法:

//擴(kuò)展KProperty的get方法為getUnsafed,其實(shí)就是去掉了out
   fun <T, R> KProperty1<T, R>.getUnsafed(receiver: Any): R {
        return get(receiver as T)
    }
//然后
println(person::class.memberProperties.map {
            it.isAccessible = true
            "${it.name}: ${it.getUnsafed(person)}"
        }.joinToString(","))
//強(qiáng)轉(zhuǎn)一下
 println(person::class.memberProperties.map {
            it.isAccessible = true
            it as KProperty1<Person, Any>
            "${it.name}: ${it.get(person)}"
        }.joinToString(","))
//這一種就涉及到kotlin中的獲取KClass的方式, 先獲取java的Class再獲取kotlin的KClass
//神奇的是,這種獲取到的it的類型沒有out,而是我們期望的 KProperty1<Person, Any>
 println(person.javaClass.kotlin.memberProperties.map {
            it.isAccessible = true
            "${it.name}: ${it.get(person)}"
        }.joinToString(","))

最后

在kotlin學(xué)習(xí)和探索的過程中發(fā)現(xiàn)了這些問題,非常地好奇,想要探知其原理。所以摸索著前進(jìn),希望能帶給其他人一些學(xué)習(xí)的思路和興趣。
另外,還有一個(gè)問題就是:
為什么有時(shí)候map的it是帶out的有時(shí)候不帶?這個(gè)跟class是java的class還是kotlin的class是否有關(guān)系?其間的原理和過程是怎么樣的,我還在繼續(xù)探索,希望明白的同學(xué)可以分享一下。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • 前言 人生苦多,快來 Kotlin ,快速學(xué)習(xí)Kotlin! 什么是Kotlin? Kotlin 是種靜態(tài)類型編程...
    任半生囂狂閱讀 26,669評(píng)論 9 118
  • Android 自定義View的各種姿勢(shì)1 Activity的顯示之ViewRootImpl詳解 Activity...
    passiontim閱讀 178,716評(píng)論 25 709
  • 第九章 Kotlin與Java混合調(diào)用 雖然 Kotlin 的開發(fā)很方便,但當(dāng)你與他人協(xié)作時(shí),總會(huì)碰到 Java ...
    光劍書架上的書閱讀 1,859評(píng)論 2 11
  • Spring Cloud為開發(fā)人員提供了快速構(gòu)建分布式系統(tǒng)中一些常見模式的工具(例如配置管理,服務(wù)發(fā)現(xiàn),斷路器,智...
    卡卡羅2017閱讀 136,502評(píng)論 19 139
  • 你在公園里看過日出嗎? 若這樣提問,多數(shù)人會(huì)顯出不屑,他們會(huì)說,看日出,最好的去處當(dāng)是泰山,當(dāng)是黃山,當(dāng)是海上,當(dāng)...
    雪琴吟閱讀 1,283評(píng)論 2 9

友情鏈接更多精彩內(nèi)容