【问题标题】:Kotlin data class: how to read the value of property if I don't know its name at compile time?Kotlin 数据类:如果我在编译时不知道其名称,如何读取属性的值?
【发布时间】:2016-06-02 04:42:10
【问题描述】:

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

【问题讨论】:

标签: kotlin data-class


【解决方案1】:

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

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

@Suppress("UNCHECKED_CAST")
fun <R> readInstanceProperty(instance: Any, propertyName: String): R {
    val property = instance::class.members
                     // 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  
}

build.gradle.kts

dependencies {
    implementation(kotlin("reflect"))
}

使用

// 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 岁了......这仍然是这样做的方法吗?我很好奇 Kotlin API 中是否有任何新内容可能会创建另一种实现此目的的方法。
  • @HatemJaber 如果您正在读取一个在编译时不知道名称的属性,那么可以。如果要读取编译时存在的已知属性,可以使用属性引用来访问该属性。
  • @HatemJaber:较新版本的反射库允许使用 instance::class.members 而不是 instance.javaClass.kotlin.declaredMemberProperties, though you should check that the type of the property is a KProperty` 或 KMutableProperty&lt;*, *&gt;
  • @HatemJaber 请检查我的回答:stackoverflow.com/a/56115232/2939453它可能对你有帮助
  • @AdamKis 如果添加了正确的导入语句,我的回答仍然有效,我已将其更新为较新的版本并包含导入。
【解决方案2】:

你可以通过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 对象(但它错过了 isconventionbooleanhas 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


第二个选项涉及使用kotlin-reflect 库,您应该将其单独添加到您的项目中,here's its documentation。它将让您获得实际的 Kotlin 属性值,而忽略 Java getter。

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

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

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

【讨论】:

  • 第一个解决方案有几个流程:它排除了继承的属性,不适用于布尔属性(其 getter 以 is... 开头),即使它们不是公共的,也会尝试访问属性。使用 java.beans 类(或任何允许访问 bean 属性的 Java 库)可能是一个更好的主意。
  • @JBNizet,谢谢你的评论,我会努力改进一下。
  • @JBNizet,修正了你所说的关于实施的问题。
  • @JBNizet,嗯,刚刚试了一下——Kotlin 甚至为Boolean 属性生成getSomething 命名方法,而不是isSomething
  • 这是我的观点的一部分:与其假设和依赖低级方法,不如依赖 Kotlin 反射支持属性(即答案的第二部分)或更高-处理 Java Bean 的级别 Java 抽象。
【解决方案3】:

上面的答案对我不起作用,所以我为此创建了一个扩展函数:

@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"

【讨论】:

【解决方案4】:

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

kCallable.returnType

但是您仍然必须明确分配泛型类型:

getField<String>

而不是

getField<kCallable.returnType>

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

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

【讨论】:

  • 您找到答案了吗?这正是我现在正在处理的问题。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2023-03-28
  • 2010-10-15
  • 1970-01-01
  • 1970-01-01
  • 2015-09-20
相关资源
最近更新 更多