我想使用 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.propD
或propB.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::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
,构建器的类型需要更改,以便类型检查起作用。这意味着必须创建一个新的构建器,并且您不能使用旧的构建器。
您可以通过重命名 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/