如何用Kotlin反射更改成员字段? [英] How to change a member field with Kotlin reflection?

查看:511
本文介绍了如何用Kotlin反射更改成员字段?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我要将一个类从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
}

???行中,我想访问MyTestObjectname属性以将其设置为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屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆