最新消息:雨落星辰是一个专注网站SEO优化、网站SEO诊断、搜索引擎研究、网络营销推广、网站策划运营及站长类的自媒体原创博客

How to set value of kotlin object-class property using kotlin.reflect API? - Stack Overflow

programmeradmin2浏览0评论

How can I set property value for classes with object keyword using kotlin.reflect API? I was hoping that this is analogous to setting values in regular classes, however, this is not the case. When setting a field value, I get an exception indicating the wrong number of parameters, which even looks to me like a bug in the language itself (I would expect that if, for some reason, setting a value for this type of class was not possible, I would rather receive an OperationNotSupported-style exception).

Here is an minimal example to reproduce the problem:

import kotlin.reflect.KClass
import kotlin.reflect.KMutableProperty1
import kotlin.reflect.full.declaredMemberProperties
import kotlin.reflect.jvm.isAccessible

fun main() {
    test(RegularClass::class, RegularClass())   //This one prints "Hello world" as expected!
    
    /* Both of these methods throw "java.lang.IllegalArgumentException: Callable expects 1 arguments, but 2 were provided"
     * How to set value of kotlin "object" property? */
    test(ObjectClass::class, null)
    test(ObjectClass::class, ObjectClass)
}

private fun test(kClass: KClass<out Greeter>, receiver: Greeter?) {
    val property = kClass.declaredMemberProperties
        .filterIsInstance<KMutableProperty1<Any?, Any?>>()
        .first {
            it.name == "variable"
        }

    property.isAccessible = true
    property.set(receiver, "Hello world!")
    receiver?.helloWorld()
}

interface Greeter {
    fun helloWorld()
}

class RegularClass: Greeter {
    private var variable: String = "Wrong value"

    override fun helloWorld() {
        println(variable)
    }
}

object ObjectClass: Greeter {
    private var variable: String = "Wrong value"

    override fun helloWorld() {
        println(variable)
    }
}

How can I set property value for classes with object keyword using kotlin.reflect API? I was hoping that this is analogous to setting values in regular classes, however, this is not the case. When setting a field value, I get an exception indicating the wrong number of parameters, which even looks to me like a bug in the language itself (I would expect that if, for some reason, setting a value for this type of class was not possible, I would rather receive an OperationNotSupported-style exception).

Here is an minimal example to reproduce the problem:

import kotlin.reflect.KClass
import kotlin.reflect.KMutableProperty1
import kotlin.reflect.full.declaredMemberProperties
import kotlin.reflect.jvm.isAccessible

fun main() {
    test(RegularClass::class, RegularClass())   //This one prints "Hello world" as expected!
    
    /* Both of these methods throw "java.lang.IllegalArgumentException: Callable expects 1 arguments, but 2 were provided"
     * How to set value of kotlin "object" property? */
    test(ObjectClass::class, null)
    test(ObjectClass::class, ObjectClass)
}

private fun test(kClass: KClass<out Greeter>, receiver: Greeter?) {
    val property = kClass.declaredMemberProperties
        .filterIsInstance<KMutableProperty1<Any?, Any?>>()
        .first {
            it.name == "variable"
        }

    property.isAccessible = true
    property.set(receiver, "Hello world!")
    receiver?.helloWorld()
}

interface Greeter {
    fun helloWorld()
}

class RegularClass: Greeter {
    private var variable: String = "Wrong value"

    override fun helloWorld() {
        println(variable)
    }
}

object ObjectClass: Greeter {
    private var variable: String = "Wrong value"

    override fun helloWorld() {
        println(variable)
    }
}
Share Improve this question asked Feb 16 at 17:56 MarcoralMarcoral 451 silver badge3 bronze badges 1
  • My usual warning to folks coming to Kotlin from more dynamic languages: in a statically-typed language like Kotlin, reflection is a code smell. It's needed for frameworks, plugins, build tools, and interoperability; but it's fragile, slow, ugly, insecure, hard to read, and turns compile-time errors into runtime ones. For general programming, there's usually a better approach. – gidds Commented Feb 16 at 23:28
Add a comment  | 

1 Answer 1

Reset to default 2

This is indeed a Kotlin bug. A private property in an object gets compiled to a Java static field, which does not need a receiver parameter to be set. There are three different issues on YouTrack regarding this issue, for getters: KT-55449, KT-55872, and KT-23267.

A workaround for now is to detect such a field, and set it using Java reflection.

private fun test(kClass: KClass<out Greeter>, receiver: Greeter?) {
    val property = kClass.declaredMemberProperties
        .filterIsInstance<KMutableProperty1<Any?, Any?>>()
        .first {
            it.name == "variable"
        }

    property.isAccessible = true
    val field = property.javaField
    if ((field?.modifiers?.and(Modifier.STATIC or Modifier.PRIVATE) ?: 0) > 0) {
        field?.set(null, "Hello World!")
    } else {
        property.set(receiver, "Hello world!")
    }
    receiver?.helloWorld()
}
发布评论

评论列表(0)

  1. 暂无评论