在Kotlin,我说data class A (val f: B)
和data class B (val f: A)
。我想初始化本地var a: A
和var b: B
,a.f
为b
,b.f
为a
。 A.f
和B.f
必须保留为val。这个循环实例化是否可能?
data class A(val f: B)
data class B(val f: A)
fun foo() {
var a: A
var b: B
// instantiate a and b here with a.f == b and b.f == a ??
}
答案 0 :(得分:4)
不完全是你想要但应该有效:
interface A {
val f: B
}
interface B {
val f: A
}
data class AImpl(override var f: B) : A
data class BImpl(override var f: A) : B
fun <T> uninitialized(): T = null as T
fun foo() {
var aImpl = AImpl(uninitialized())
var bImpl = BImpl(aImpl)
aImpl.f = bImpl
val a: A = aImpl
val b: B = bImpl
}
如果您不关心数据类属性是val
,那么您可以摆脱接口并仅使用实现类。
答案 1 :(得分:2)
Dmitry Jemerov在kotlinlang Slack小组的回答:
唯一的可能是使用反射。将一个虚拟B实例传递给A的构造函数,然后创建一个B的实例,将A实例作为参数传递,然后使用反射将A实例中“f”字段的值更改为B实例。
但我强烈建议你不要这样做,而是重新考虑你的数据模型。
答案 2 :(得分:1)
将一个尚未构建的对象作为参数传递似乎是不可能的。所以,我认为这种交叉引用初始化不适用于原始数据类。
但是可以采取一些解决方法:
data class A(val f: A.() -> B)
data class B(val f: B.() -> A)
val A.b: B get() = f(this)
val B.a: A get() = f(this)
fun test() {
val O = object {
val refA: A = A { refB }
val refB: B = B { refA }
}
var a = O.refA
var b = O.refB
// validating cross-refs
require( a === b.a )
require( b === a.b )
require( b === b.a.b )
require( a === a.b.a )
println("Done.")
}
答案 3 :(得分:1)
如果您将自引用val
明确声明为Lazy
,则可以这样做:
sealed class MyData {
data class A(val x: Int) : MyData()
data class B(val x : Int, val rb: Lazy<MyData>) : MyData() {
val r: MyData by rb
}
}
fun <A : Any> rec(body: (Lazy<A>) -> A): A {
lateinit var a: A
a = body(lazy { a })
return a
}
fun MyData.print(gas: Int): String = if (gas <= 0) "..." else
when(this) {
is MyData.A -> "A(x=$x)"
is MyData.B -> {
val rbString =
if (rb.isInitialized())
r.print(gas - 1)
else
"<thunk>"
"B(x=$x, rb=$rbString)"
}
}
fun main() {
val a = MyData.A(42)
val b1 = MyData.B(1, lazy { a })
println(b1.r) // Force value
println(b1)
val b2 = rec<MyData.B> { b2 -> MyData.B(1, b2) }
println(b2.r.print(4))
}
此打印
A(x=42)
B(x=1, rb=A(x=42))
B(x=1, rb=B(x=1, rb=B(x=1, rb=B(x=1, rb=...))))