如何用Kotlin反射更改成员字段? [英] How to change a member field with Kotlin reflection?
问题描述
我要将一个类从Java移植到Kotlin.此类声明了数百个对象.每个对象都有一个name属性,该属性与该对象的声明变量名称相同. Java反射允许通过反射使用声明的名称来设置对象成员name
.只需在数百个构造函数中保存一个参数即可.
I'm porting a class from Java to Kotlin. This class declares hundreds of objects. Each object has a name property which is identical with the declared variable name of the object. Java reflection allows to use the declared name via reflection to set the object member name
. Just saves one parameter in hundreds of constructors.
我尝试在Kotlin中进行相同的操作,但无法弄清楚如何进行属性设置.这是一些简化的测试代码:
I try to do the same in Kotlin but can't figure out how to do the property setting. Here is some simplified test code:
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
}
在???
行中,我想访问MyTestObject
的name
属性以将其设置为member.name
.我正在寻找类似于(member.toObject() as MyTestObject).name = member.name
的功能.
The ???
line is where I would like to get access to the name
property of MyTestObject
to set it to to member.name
. I'm looking for a function similar to (member.toObject() as MyTestObject).name = member.name
.
推荐答案
尽管kotlin-reflection
努力做到类型安全,但有时类型系统和推理逻辑不足以允许您尝试执行类似的操作以类型安全的方式进行.因此,您必须进行未经检查的强制类型转换,声明您对类型的了解超出了编译器可以推断的范围.
While kotlin-reflection
strives to be type-safe, sometimes the type system and the inference logic are not enough to allow for the things like what you are trying to do in a type-safe way. So, you have to make unchecked casts, stating that your knowledge about the types is more than the compiler can infer.
在您的情况下,强制转换member
就足够了,这样您就可以将伴随对象实例传递到其.get(...)
并将结果用作MyTestObject
,将// ???
行替换为:
In your case, it's enough to cast member
so that you can pass the companion object instance into its .get(...)
and use the result as a MyTestObject
, replace the // ???
line with:
@Suppress("UNCHECKED_CAST")
(member as KProperty1<Any, MyTestObject>)
.get(MyTestObject::class.companionObject!!.objectInstance!!)
.name = member.name
如果您可以将MyTestObject::class.companionObject!!
替换为MyTestObject.Companion::class
(即您的实际用例不涉及从不同的类中获取.companionObject
),则不需要未经检查的强制类型转换,并且可以将上面的语句替换为: /p>
If you can replace MyTestObject::class.companionObject!!
with MyTestObject.Companion::class
(i.e. your actual use case does not involve getting .companionObject
from different classes), the unchecked cast is not needed, and you can replace the statement above with this:
(member.get(MyTestObject.Companion) as MyTestObject).name = member.name
作为一种完全不需要伴随对象反射的替代方法,您可以对委托执行相同的绑定逻辑. 实施provideDelegate
可让您自定义初始化属性的逻辑,然后可以在其中分配名称:
As an alternative that does not require companion object reflection at all, you can do the same binding logic with the delegation. Implementing provideDelegate
allows you to customize the logic of initializing the property, and that's where you can assign the names:
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()
这篇关于如何用Kotlin反射更改成员字段?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!