1. 程式人生 > >【Kotlin】Kotlin與Java反射實踐

【Kotlin】Kotlin與Java反射實踐

Google發話Kotlin成為Android開發的一級語言,然後Kotlin就瞬間火了起來,各種教程什麼的也席捲而來,不過大部分都差不多,語法、用法、規範,或是官方文件、或是中文翻譯,無論看哪個都一樣。

本篇主要是在學習中的對Kotlin反射的一點記錄,在開發Android過程中難免會用到反射來呼叫類的private方法或獲取private屬性等,Kotlin的教程上只是大致描述了一下,看了還是有點懵。

實踐

實踐是檢驗整理的唯一標準。

首先準備一個User類,當然是Kotlin實現的啦,username、nickname(私有)、age三個屬性,isChild(私有),toString方法(用java反射的時候屬性的get、set方法會自己生成)

class User constructor() {
    var username: String? = null
    private var nickname: String? = null
    var age: Int? = null

    constructor(username: String, nickname: String, age: Int?) : this() {
        this.username = username
        this.nickname = nickname
        this.age = age
    }

    private fun isChild(): Boolean {
        return age!! <= 8
    }

    override fun toString(): String {
        return "User{" +
                "username='" + username + '\'' +
                ", nickname='" + nickname + '\'' +
                ", age=" + age +
                '}'
    }
}

屬性反射

1、先建立User物件設定username和age,

2、通過反射獲取所有屬性

3、對私有屬性nickname設定值為Jack

4、列印User物件

Java實現

    public static void reflectField() {
        User user = new User();
        user.setUsername("123456");
        user.setAge(10);
        Field[] fields = User.class.getDeclaredFields();
        for (Field field : fields) {
            Log.i(TAG, "fieldName = " + field.getName());
            try {
                if("nickname".equals(field.getName())){
                    field.setAccessible(true);
                    field.set(user, "Jack");
                }
            } catch (IllegalAccessException e) {
                e.printStackTrace();
            }
        }
        Log.i(TAG, user.toString());
    }
日誌列印



通過外掛轉換的Kotlin實現

可以看到通過外掛轉換的Kotlin就只是用Kotlin使用Java方法而已

    fun reflectField() {
        val user = User()
        user.username = "123456"
        user.age = 10
        val fields = User::class.java.declaredFields
        for (field in fields) {
            Log.i(TAG, "fieldName = " + field.name)
            try {
                if ("nickname" == field.name) {
                    field.isAccessible = true
                    field.set(user, "Jack")
                }
            } catch (e: IllegalAccessException) {
                e.printStackTrace()
            }

        }
        Log.i(TAG, user.toString())
    }

Kotlin實現

這裡使用了Kotlin中KClass裡的declaredMemberProperties方法來獲取類內部所有屬性,它還有declaredMemberExtensionProperties方法是用於獲取類的擴充套件屬性的,還有其他一些經過過濾的屬性。最後的設定還是呼叫了Java的Field,set和get的使用都很簡單

    fun reflectField() {
        var user = User()
        user.username = "123456"
        user.age = 10
        user::class.declaredMemberProperties.forEach {
            Log.i(TAG, "fieldName = ${it.name}")
            if ("nickname".equals(it.name)) {
                it.isAccessible = true
                it.javaField?.set(user,"jack")
            }
        }
        Log.i(TAG,user.toString())
    }
日誌列印


一模一樣的日誌資訊,通過Kotlin原生方法也設定了私有屬性的值

方法反射

1、先建立User物件設定username和age,
2、通過反射獲取所有方法
3、呼叫私有方法isChild

Java實現

    public static void reflectMethod() {
        User user = new User();
        user.setUsername("123456");
        user.setAge(10);
        Method[] methods = User.class.getDeclaredMethods();
        for (Method method : methods) {
            Log.i(TAG, "methodName = " + method.getName());
            if (method.getName().contains("isChild")) {
                method.setAccessible(true);
                try {
                    Log.i(TAG, "isChild = " + method.invoke(user));
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
        }
    }
日誌列印


Kotlin實現

使用Kotlin的KClass類中declaredFunctions獲取類內所有方法,KClass類中還提供了好多經過過濾的方法,staticFunctions(靜態方法,包括父類),memberFunctions(非擴充套件非靜態方法,包括父類),memberExtensionFunctions(擴充套件方法,包括父類),declaredFunctions(非靜態方法),declaredMemberFunctions(非擴充套件非靜態方法),declaredMemberExtensionFunctions(擴充套件方法),對以上只能說非常全面。。。

    fun reflectMethod() {
        var user = User()
        user.username = "123456"
        user.age = 10
        user::class.declaredFunctions.forEach {
            Log.i(TAG, "methodName = ${it.name}")
            if("isChild".equals(it.name)){
                it.isAccessible = true
                Log.i(TAG, "isChild = ${it.javaMethod?.invoke(user)}")
            }
        }
    }
日誌列印

這裡可以發現Kotlin列印的方法中並沒有get和set方法



注:

最重要的一點就是Kotlin使用反射需要單獨引用

    compile "org.jetbrains.kotlin:kotlin-reflect:$kotlin_version"
最簡單明瞭的Kotlin使用反射的方法