Kotlin数据类:如果我在编译时不知道它的名字,如何读取属性的值?

时间:2016-02-20 15:17:13

标签: kotlin data-class

如果属性名称仅在运行时已知,我如何读取Kotlin数据类实例中的属性值?

4 个答案:

答案 0 :(得分:15)

你可以通过reflection来完成它,对于数据类和普通类都是一样的。

第一个选项就是使用Java反射:

val name = obj.javaClass
              .getMethod("getName") // to get property called `name`
              .invoke(obj)

您甚至可以制作扩展功能:

inline fun <reified T : Any> Any.getThroughReflection(propertyName: String): T? {
    val getterName = "get" + propertyName.capitalize()
    return try {
        javaClass.getMethod(getterName).invoke(this) as? T
    } catch (e: NoSuchMethodException) {
        null
    }
}

它称为公众吸气剂。要获取私有属性的值,您可以使用getDeclaredMethodsetAccessible修改此代码。这也适用于具有相应getter的Java对象(但它错过is的{​​{1}}和has boolean的getter。

用法:

data class Person(val name: String, val employed: Boolean)

val p = Person("Jane", true)
val name = p.getThroughReflection<String>("name")
val employed = p.getThroughReflection<Boolean>("employed")

println("$name - $employed") // Jane - true

<小时/> 第二个选项涉及使用convention库,您应该单独添加到项目kotlin-reflect。它将让你获得实际的Kotlin属性值,忽略Java getter。

您可以使用javaClass.kotlin获取实际的Kotlin类令牌,然后从中获取属性:

val name = p.javaClass.kotlin.memberProperties.first { it.name == "name" }.get(p)

此解决方案仅适用于Kotlin类,而不适用于Java类,但如果您需要使用Kotlin类,它将更加可靠:它不依赖于底层实现。

答案 1 :(得分:11)

这是一个从给定属性名称的类实例中读取属性的函数(如果找不到属性,抛出异常,但您可以更改该行为):

git mergetool

示例用法:

import kotlin.reflect.KProperty1
import kotlin.reflect.full.memberProperties

@Suppress("UNCHECKED_CAST")
fun <R> readInstanceProperty(instance: Any, propertyName: String): R {
    val property = instance::class.memberProperties
                     // don't cast here to <Any, R>, it would succeed silently 
                     .first { it.name == propertyName } as KProperty1<Any, *> 
    // force a invalid cast exception if incorrect type here
    return property.get(instance) as R  
}

注意: 使用Kotlin反射需要// some data class data class MyData(val name: String, val age: Int) val sample = MyData("Fred", 33) // and reading property "name" from an instance... val name: String = readInstanceProperty(sample, "name") // and reading property "age" placing the type on the function call... val age = readInstanceProperty<Int>(sample, "age") println(name) // Fred println(age) // 33 依赖项。

答案 2 :(得分:1)

我想知道是否可以通过编程方式定义字段的类型。您可以通过以下方式轻松获得类型:

kCallable.returnType

但是您仍然必须显式地分配泛型类型:

getField<String>

代替

getField<kCallable.returnType>

编辑: 我最终使用了以下内容:

when (prop.call(object)) {
    is ObservableList<*> -> {}
    is Property<*> -> {}
}

答案 3 :(得分:0)

上面的答案对我不起作用,因此我为此创建了扩展功能:

@Throws(IllegalAccessException::class, ClassCastException::class)
inline fun <reified T> Any.getField(fieldName: String): T? {
    this::class.memberProperties.forEach { kCallable ->
        if (fieldName == kCallable.name) {
            return kCallable.getter.call(this) as T?
        }
    }
    return null
}

这是一个示例调用:

val valueNeeded: String? = yourObject.getField<String>("exampleFieldName")

还将其包含在您应用的build.gradle中:

implementation "org.jetbrains.kotlin:kotlin-reflect:$kotlin_version"