在 Kotlin 中通过 fieldName 访问对象值

Accessing an objects value by fieldName in Kotlin

我知道我可以做这样的事情:

data class Foo(val bar: String)

val foo = Foo("bar")

foo::class.java.getDeclaredField("bar").let {  it.trySetAccessible(); it.get(foo) }

为此,我需要使用 class.java.getDeclaredField("bar") 和其他 Java 东西,例如 trySetAccessible()get

是否可以用更 Kotlin 的方式来做到这一点?

您可以像这样使用 Kotlin 反射来完成:

val prop = (foo::class as KClass<Foo>).memberProperties.single { it.name == "bar" }
println(prop.get(foo))

请注意,与 Java 相反,您不能使用此技术来设置只读字段 (val) 的值。原因是:在 Kotlin 中,我们真正处理的不是字段,而是属性。 属性 是一个 getter 和一个可选的 setter。如果没有 setter 那么就没有真正可以调用的东西。 val 可能没有字段支持,它可能根本没有任何价值。在这种情况下,甚至考虑设置 属性.

的值也没有意义

当然,如果 属性 由一个支持,Kotlin 反射 API 可以提供对字段的可选访问,但现在我相信 public API.

Kotlin 反射没有 getDeclaredField() 这样的函数。相反,它具有返回集合的函数,例如 getMemberProperties(),您可以使用 Iterable 扩展函数来查找您需要的内容。将 class 的实例作为要使用的 getter/setter 的第一个参数传递。

    val value = Foo::class.memberProperties
        .first { it.name == "bar" }
        .also { it.isAccessible = true } // if it's not visible
        .getter(foo)

请注意,要在不强制转换的情况下执行此操作,您需要使用 Foo::class 而不是 foo::class,因为后者是 Class<out Foo>,因此 returns 的吸气剂不可用,因为方差。

但是如果你使用反射,你可能不知道你正在使用的 class,在这种情况下,你必须在使用它之前转换返回的 属性。

    val value = foo::class.memberProperties
        .first { it.name == "bar" }
        .also { it.isAccessible = true }
        .let { it as KProperty1<in Any, *> }
        .getter(foo)

// or

    val value = (foo::class as KClass<in Any>).memberProperties
        .first { it.name == "bar" }
        .also { it.isAccessible = true }
        .getter(foo)