kotlin - 如何使用成员引用在 Kotlin 中创建属性的分层路径

标签 kotlin generics kotlin-reflect

我想使用 Kotlin 成员引用创建从根元素 A 到其声明的成员属性的路径的字符串表示形式,直到到达特定的叶元素。为了澄清一下,假设我有一个这样的类结构:

data class A (val propB: B)
data class B (val propC: C)
data class C (val propD: D, val propE: E)

最后,我想创建一个字符串,其中包含例如:propB.propC.propDpropB.propC.propE。它与 JSONPath 的概念基本相同。

这种关系的上下文在编译时是已知的,因此可以直接对其进行硬编码,但如果在重构期间重命名这些属性之一,则会失败,因此我想使用直接引用的编程方法。

我可以使用 A::propB.name 访问此层次结构的第一个“级别”,它将简单地打印 propB。但是,我找不到链接这些调用的好方法,因为 A::propB::propC.name 等是无效代码。

我尝试编写一个 Builder 类来帮助我将这些引用链接在一起,但它不起作用:

class RefBuilder<R : Any>(val path: String = "", val next: KClass<R>) {

    companion object {
        inline fun <T, reified R : Any> from(start: KProperty1<T, R>): RefBuilder<R> {
            return RefBuilder(start.name, R::class)
        }
    }

    inline fun <reified N : Any> add(nextRef: (KClass<R>) -> KProperty1<R,N>): RefBuilder<N> {
        return RefBuilder("$path.${nextRef.invoke(this.next).name}", N::class)
    }

    override fun toString() = path
}

fun main() {
    val builder = RefBuilder.from(A::propB)
    builder.add { it::propC } // this doesn't work
    println(builder)          // should print: "propB.propC"
}

如果有任何帮助,我将不胜感激。也许还有一种我错过的更简单的解决方案。

最佳答案

这修复了您的代码:

val builder = RefBuilder.from(A::propB).add { B::propC }
println(builder)

您只需要使用 B::propC而不是it::propCKClass没有 propC属性。

我认为这是您错过的“简单”解决方案 - 为什么不直接采用 KProperty1直接,而不是 (KClass<R>) -> KProperty1<R,N> ?您也不需要 reified: Any限制条件。

class RefBuilder<R>(val path: String = "") {

    companion object {
        fun <R> from(start: KProperty1<*, R>): RefBuilder<R> {
            return RefBuilder(start.name)
        }
    }

    fun <N> add(nextRef: KProperty1<R,N>): RefBuilder<N> {
        return RefBuilder("$path.${nextRef.name}")
    }

    override fun toString() = path
}

fun main() {
    val builder = 
        RefBuilder.from(A::propB)
            .add(B::propC)
    println(builder) // prints: "propB.propC"
}

请注意您所展示的用法 - 创建一个构建器,调用 add在其上,然后打印原始构建器 - 是不可能的。这是因为每次调用add ,构建器的类型需要更改,以便类型检查起作用。这意味着必须创建一个新的构建器,并且您不能使用旧的构建器。

您可以通过重命名 add 使语法更清晰进入plus ,并将其变成operator fun ,然后添加此 plus运算符:

operator fun <T, U, V> KProperty1<T, U>.plus(rhs: KProperty1<U, V>) = RefBuilder.from(this) + rhs

然后您可以使用如下语法:

println(A::propB + B::propC + C::propD)

另一个在这种情况下非常合适的运算符,如果你找到 +有点令人困惑,是 //看起来有点像您正在文件系统中的目录层次结构中向下移动。

关于kotlin - 如何使用成员引用在 Kotlin 中创建属性的分层路径,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/73649139/

相关文章:

reflection - 获取可空类型的类

kotlin - 如何在 Kotlin 中对列表进行分区和类型转换

scala - 应用于 Scala 函数的类型参数

android - 如何在 Kotlin 中处理 "lock"静态对象

swift - 高阶函数赋值

Java 反射泛型 - 通过在编译时传递参数进行转换?

android - Proguard 使用 kotlin-reflect 为所有 INSTANCE 字段生成注释

kotlin - 如何使用反射更改 kotlin 私有(private)验证?

android - DatePickerDialog 已将 androidx.fragment.app.DialogFragment 中的按钮涂黑

android - 如何在 Kotlin 中注入(inject)原始变量?