2017-10-20 30 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) 
} 
相关问题