2017-10-20 232 views
1

我正在從Java移植到Kotlin的一個類。這個類聲明瞭數百個對象。每個對象都有一個名稱屬性,它與對象的聲明變量名稱相同。 Java反射允許通過反射使用聲明的名稱來設置對象成員name。只需在數百個構造函數中保存一個參數即可。如何使用Kotlin反射更改成員字段?

我嘗試在Kotlin中做同樣的事情,但無法弄清楚如何做屬性設置。下面是一些簡單的測試代碼:

package myPackage 

import kotlin.reflect.full.companionObject 
import kotlin.reflect.full.declaredMemberProperties 

class MyTestObject() { 

    var name: String = "NotInitialized" 

    companion object { 
    val Anton = MyTestObject() 
    val Berta = MyTestObject() 
    val Caesar = MyTestObject() 
    } 
} 

fun main(args : Array<String>) { 
    println(MyTestObject.Anton.name) // name not yet initialized 

    // Initialize 'name' with the variable name of the object: 
    for (member in MyTestObject::class.companionObject!!.declaredMemberProperties) { 
    if (member.returnType.toString() == "myPackage.MyTestObject") { 
     println("$member: ${member.name}") 

     // Set 'name' property to 'member.name': 
     // ??? 
    } 
    } 

    println(MyTestObject.Anton.name) // now with the initialized name 
} 

???線是我想獲得訪問的MyTestObjectname屬性將其設置爲以member.name。我正在尋找類似於(member.toObject() as MyTestObject).name = member.name的功能。

回答

2

雖然kotlin-reflection努力成爲類型安全的,但有時類型系統和推理邏輯不足以允許您以類型安全的方式嘗試執行的操作。所以,你必須做出不加控制的轉換,說明你對類型的瞭解遠不止是編譯器可以推斷的。

在你的情況,這足以施展member,這樣就可以通過同伴對象實例到其.get(...)和使用結果作爲MyTestObject,與更換// ???行:

@Suppress("UNCHECKED_CAST") 
(member as KProperty1<Any, MyTestObject>) 
    .get(MyTestObject::class.companionObject!!.objectInstance!!) 
    .name = member.name 

如果你能取代MyTestObject::class.companionObject!!MyTestObject.Companion::class(即您的實際使用案例不涉及從不同類別獲取.companionObject),則不需要進行檢查,您可以用上述語句替換上述語句:

(member.get(MyTestObject.Companion) as MyTestObject).name = member.name 

作爲根本不需要配套對象反射的替代方法,您可以對代理執行相同的綁定邏輯。 Implementing provideDelegate允許您自定義初始化屬性的邏輯,那就是你可以手動指定名稱:

operator fun MyTestObject.provideDelegate(
    thisRef: MyTestObject.Companion, 
    property: KProperty<*> 
) = apply { name = property.name } 

operator fun MyTestObject.getValue(
    thisRef: MyTestObject.Companion, 
    property: KProperty<*> 
) = this 

然後作爲

val Anton by MyTestObject() 
val Berta by MyTestObject() 
val Caesar by MyTestObject() 
+0

你的第一個例子工作。我在同一個類MyTestObject中嘗試了第二個,但get()總是提供null。還沒有嘗試委託的例子,因爲我不熟悉這種方法,它往往會使軟件過於複雜和不可讀。 –

+0

如果我在屬性聲明之後將伴隨對象內的代碼作爲init代碼移動,則第二個示例將起作用。這看起來像一個簡單易讀的解決方案。感謝您提供此解決方案! –

0

這裏聲明你的屬性是基於熱鍵的解決方案最終測試代碼:

package myPackage 

import kotlin.reflect.full.declaredMemberProperties 

class MyTestObject() { 

    lateinit var name: String 

    companion object { 
    val Anton = MyTestObject() 
    val Berta = MyTestObject() 
    val Caesar = MyTestObject() 

    init { 
     for (member in MyTestObject.Companion::class.declaredMemberProperties) { 
     if (member.returnType.toString() == "myPackage.MyTestObject") { 
      (member.get(MyTestObject.Companion) as MyTestObject).name = member.name 
     } 
     } 
    } 
    } 
} 

fun main(args : Array<String>) { 
    println(MyTestObject.Anton.name) 
    println(MyTestObject.Caesar.name) 
}