我将一个类从Java移植到Kotlin。该类声明了数百个对象。每个对象都有一个name属性,该属性与对象的声明变量名相同。 Java反射允许通过反射使用声明的名称来设置对象成员name
。只需在数百个构造函数中保存一个参数。
我尝试在Kotlin中做同样的事情,但无法弄清楚如何进行属性设置。这是一些简化的测试代码:
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
}
???
行是我希望访问name
的{{1}}属性以将其设置为MyTestObject
的位置。我正在寻找类似于member.name
的功能。
答案 0 :(得分:3)
虽然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()
答案 1 :(得分: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)
}