Kotlin 之反射詳解

  • 反射是允許程序在運行時訪問程序結構的一類特性

  • 程序結構包括:類,接口,方法,屬性等語法特性

Kotlin 字節做了一套反射API庫,這個庫需要進行依賴

implementation("org.jetbrains.kotlin:kotlin-reflect")

反射的常見用途

  • 列出類型的所有方法,屬性,內部類等等
  • 調用給定名稱及簽名方法或指定名稱的屬性
  • 通過簽名信息獲取泛型實參的具體類型
  • 訪問運行時註解及其信息完成注入或者配置操作

反射常用的數據結構

數據結構 概念及說明使用
KType 描述未擦除的類型或泛型參數等,例如Map<String,Int>;可以通過typeOf或者一些類型獲取對應父類,屬性,函數參數等
KClass 描述對象的實際類型,不包含泛型參數,例如 Map;可通過對象,類型名直接獲得
KProperty 描述屬性,可通過屬性引用,屬性所在類的 KClass 獲取
KFunction 描述函數,可通過函數引用,函數所在類的 KClass 獲取

獲取泛型實參

fun main() {
    /**
     * declaredFunctions:獲取該類中所有的函數
     * first:判斷是否爲指定的函數名,如果相等則返回出去
     * returnType:獲取返回值
     * arguments:返回值的泛型
     */
    Api::class.declaredFunctions.first { it.name == "getUser" }
        .returnType.arguments.forEach {
        println(it.type)
    }

    //直接拿到函數引用,其他的和上面一樣
    Api::getUser.returnType.arguments.forEach {
        println(it)
    }

    val subType = SubType()
    subType.typeParameter.let { println(it) }
}

class UserDTO

interface Api {
    fun getUser(): List<UserDTO>
}


abstract class SuperType<t> {
    val typeParameter by lazy {
        /**
         * 抽象類不能有實例,所以這裏的 this 是子類的
         * supertypes:獲取父類類型列表
         * first:獲取第一個
         * arguments:拿到泛型
         * type:類型
         */
        this::class.supertypes.first().arguments.first().type!!
    }
    val typeParameterJava by lazy {

    }
}

class SubType : SuperType<String>()

案例:深拷貝

data class Person(val id: Int, val name: String, val group: Group)

data class Group(val id: Int, val name: String, val location: String)

fun main() {

    val person = Person(
        0,
        "345",
        Group(0, "Kotlin.cn", "China")
    )
    val deep = person.deepCopy()


    val copied = person.copy()


    println(person.group === copied.group)
    println(person.group === deep.group)
    println(deep)
}

fun <T : Any> T.deepCopy(): T {
    //如果不是數據類就直接返回
    if (!this::class.isData) {
        return this
    }
    /**
     * primaryConstructor:獲取該類的主構造函數,如果沒有返回 null
     */
    return this::class.primaryConstructor!!.let { primaryConstuctor ->
        /**
         * parameters:構造方法中的參數
         */
        primaryConstuctor.parameters.map { parameter ->

            /**
             * memberProperties:,類中所有非擴展屬性
             */
            val value = (this::class as KClass<T>).memberProperties.first { it.name == parameter.name }.get(this)
            //屬性是否爲數據類
            if ((parameter.type.classifier as? KClass<*>)?.isData == true) {
                //如果是則進行深度拷貝
                parameter to value?.deepCopy()
            } else {
                parameter to value
            }
          //call/callBy 創建對象                                
        }.toMap().let(primaryConstuctor::callBy)
    }
}

案例:映射

data class UserVO(val login: String, val avatarUrl: String)

data class UserDTO(
    var id: Int,
    var login: String,
    var avatarUrl: String,
    var url: String,
    var htmlUrl: String
)

fun main() {
    val userDTO = UserDTO(
        0,
        "345",
        "https://www.baidu.com",
        "https://github",
        "https://httpurl"
    )

    val userVo: UserVO = userDTO.mapAs()
    println(userVo)

    val userMap = mapOf(
        "id" to 0,
        "login" to "1",
        "avatarUrl" to "2",
        "url" to "3"
    )
    val userVOFromMap: UserVO = userMap.mapAs()
    println(userVOFromMap)
}

inline fun <reified From : Any, reified TO : Any> From.mapAs(): TO {
    return From::class.memberProperties.map {
        it.name to it.get(this)
    }.toMap().mapAs()
}

inline fun <reified TO : Any> Map<String, Any?>.mapAs(): TO {
    return TO::class.primaryConstructor!!.let {
        it.parameters.map { kParameter ->
            //如果接受null,則返回,否則拋出異常
            //this[kParameter.name] :從當前的 map 中尋找。如果找到了則就是拿到了 value,否則異常
            kParameter to (this[kParameter.name] ?: if (kParameter.type.isMarkedNullable) null
            else throw IllegalArgumentException("失敗"))
        }.toMap().let(it::callBy)//構建對象
    }
}

案例:釋放對象引用不可空類型

class ReleasableNotNull<T : Any> {

    private var value: T? = null

    operator fun getValue(thisRef: Any, kProperty: KProperty<*>): T {
        return value ?: throw IllegalArgumentException("使用錯誤")
    }

    operator fun setValue(thisRef: Any, kProperty: KProperty<*>, value: T) {
        this.value = value
    }

    fun isInitialized() = value != null

    fun release() {
        value = null
    }
}

//擴展屬性
inline val KProperty0<*>.isInitialized: Boolean
    get() {
        isAccessible = true
        return (this.getDelegate() as ReleasableNotNull<*>).isInitialized()
    }

//擴展方法
fun KProperty0<*>.release() {
    isAccessible = true
    (this.getDelegate() as ReleasableNotNull<*>).release()
}

class Bitmap(val with: Int, val height: Int)

class Activity {
    private var bitmap by ReleasableNotNull<Bitmap>()

    fun onCreate() {
        println(::bitmap.isInitialized)
        bitmap = Bitmap(234, 353)
        println(::bitmap.isInitialized)
    }

    fun onDestroy() {
        println(::bitmap.isInitialized)
        ::bitmap.release()
        println(::bitmap.isInitialized)
    }
}

fun main() {
    val activity = Activity()
    activity.onCreate()
    activity.onDestroy()
}
false
true
true
false

通過反射調用構造函數、

fun main() {

    //獲取對象的class
    val cls = Person(23, "張三").javaClass.kotlin

    //調用構造器 2
    val cons = cls.primaryConstructor
    val per1 = cons?.call(100, "李四")
    println(per1)
    //2
    val per2 = cls.primaryConstructor.let {
        var i = 0
        val s = it!!.parameters.map {
            i++
            when (i) {
                1 -> it to 2
                2 -> it to "哈哈"
                else ->
                    it to ""
            }

        }.toMap()
        it.callBy(s)
    }
    println(per2)


}

class Person(val age: Int, val name: String) {

    val p = 1

    fun a() {}

    private fun b() {}

    override fun toString(): String {
        return "姓名:$name 年齡:$age"
    }
}
  • 反射
    • 反射是啥
    • 數據結構:KType,KClass,KProperty,KFunction
    • 獲取泛型實參
      • 瞭解泛型的實現機制
      • 料及實參在字節碼中的存儲機制
      • 掌握反射獲取對應類型的 KType 進而讀取泛型實參的方法
    • 爲數據類添加深拷貝
    • 反射調用構造器構造數據類實例
    • 實例之間的轉換
    • 可釋放引用的不可空類型

參考自慕課網視頻

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章