我希望使用Kotlin成员引用创建从根元素A到其声明的成员属性的路径的字符串表示形式,直到到达特定的叶元素.为澄清起见,假设我有一个如下所示的类 struct :

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来访问此层次 struct 的第一个"级别",它将简单地打印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::propC.KClass没有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的调用,构建器needs的类型都要改变,以便进行类型判断.这意味着必须创建新的构建器,而不能使用旧的构建器.

您可以通过将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)

另一个在这种情况下非常合适的运算符是/,如果您觉得+有点困惑的话./看起来有点像是沿着文件系统中目录的层次 struct 往下走.

Kotlin相关问答推荐

如何访问方法引用的接收者?

Android Studio中的只读集合不支持操作失败的原因是什么?

禁用 Gradle执行消息

Java/Kotlin中类似Rust般的注释编译?

如何在 Kotlin 中为类方法调用传递变量

协程子作业(job)取消

Kotlin 可空泛型

嵌套数组 indexOf()

为什么 IntelliJ Idea 无法识别我的 Spek 测试?

Kotlin val difference getter override vs assignment

参数不匹配;SimpleXML

kotlin 委托有什么用?

Kotlin:使用自定义设置器时没有lateinit的解决方法?

Dagger +Kotlin 不注入

判断EditText是否为空kotlin android

Android Studio - java.io.IOException:无法生成 v1 签名

Android studio 4.0 新更新版本说 Kotlin 与这个新版本不兼容

Android room DAO 接口不适用于继承

如何将 Kotlin 的 `with` 表达式用于可空类型

Kotlin反射不可用