在Kotlin中使用val的循环引用

时间:2016-02-16 17:40:16

标签: immutability kotlin circular-reference

在Kotlin,我说data class A (val f: B)data class B (val f: A)。我想初始化本地var a: Avar b: Ba.fbb.faA.fB.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 ??
}

4 个答案:

答案 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=...))))