如何通过反射在Kotlin中设置伴侣对象的属性?

时间:2017-07-12 11:41:13

标签: reflection kotlin

当我有一个具有伴随对象的类时,是否可以使用反射在此伴随对象中设置属性?我可以使用普通属性来执行此操作,但在随播对象上失败:

import kotlin.reflect.KMutableProperty
import kotlin.reflect.full.companionObject
import kotlin.reflect.full.memberProperties

class WithProperty {
    lateinit var prop: String

    companion object {
        lateinit var companionProp: String
    }

    fun test() = "$companionProp $prop"
}

fun main(args: Array<String>) {
    val obj = WithProperty()

    val prop = obj::class.memberProperties.filter { it.name == "prop" }.first()
    if (prop is KMutableProperty<*>) {
        prop.setter.call(obj, "world")
    }

    val companion = obj::class.companionObject
    if (companion != null) {
        val companionProp = companion.memberProperties.filter { it.name == "companionProp" }.first()
        if (companionProp is KMutableProperty<*>) {
            companionProp.setter.call(companionProp, "hello") // <-- what must go here as first argument?
        }
    }

    println(obj.test())
}

调用普通属性的setter可以正常工作,但是当我调用companionProp.setter.call(companionProp, "hello")时,我得到了

  

线程“main”中的异常java.lang.IllegalArgumentException:object不是声明类的实例

我必须作为第一个参数传递给call()才能成功?

编辑:我写了companionProp作为第一个参数,但这肯定是错误的,我实际上尝试使用companion对象,但这样做也不行。< / p>

2 个答案:

答案 0 :(得分:3)

  

object不是声明类

的实例

就像在Java中一样,在调用反射方法时,需要将对象本身作为第一个参数传递。

call的第一个参数应该是伴随对象,因为那是您要修改其属性的对象。

您正在传递随播广告的类对象而不是随播广告对象本身。

可以通过ClassName.Companion或通过KClass#companionObjectInstance使用进一步反射来访问随播广告对象。

companionProp.setter.call(WithProperty.Companion, "hello")
companionProp.setter.call(obj::class.companionObjectInstance, "hello")
companionProp.setter.call(WithProperty::class.companionObjectInstance, "hello")

运行时,两个变体都按预期打印hello world

请注意,如果配对对象不存在,Foo.Companion将导致编译错误,而反射变体将返回null

答案 1 :(得分:2)

第一个参数是声明类的实例。

您传递的是KProperty实例companionProp而不是同伴对象实例。但是,您可以使用KClass.companionObjectInstance获取公司实例。例如:

//a non-static property having a receiver, so it should be a KMutableProperty1 here
//                   v 
if (companionProp is KMutableProperty1<*, *>) {
//  get the companion object instance ---v
    companionProp.setter.call(obj::class.companionObjectInstance, "hello") 
}