不变性.kotlin中带vals的循环引用

qco9c6ql  于 2021-07-23  发布在  Java
关注(0)|答案(4)|浏览(573)

在Kotlin,说我有 data class A (val f: B) 以及 data class B (val f: A) . 我想初始化本地 var a: A 以及 var b: B 以至于 a.fb 以及 b.fa . 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 ??
}
gab6jxml

gab6jxml1#

不完全是你想要的,但应该工作:

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 s、 您可以去掉接口,只使用实现类。

kq0g1dla

kq0g1dla2#

dmitry jemerov在kotlinlang slack集团的回答:
唯一的可能就是使用反射。将一个伪b示例传递给a的构造函数,然后创建一个传递a示例作为参数的真实b示例,然后使用反射将a示例中“f”字段的值更改为b示例。
但我强烈建议您不要这样做,而是重新考虑您的数据模型。

sbdsn5lh

sbdsn5lh3#

传递一个还没有被构造成论点的对象似乎是不可能的。所以,我认为这样的交叉引用初始化对于原始数据类是不可能的。
不过,还是可以解决一些问题:

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.")
}
syqv5f0l

syqv5f0l4#

如果显式声明自引用,就可以这样做 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=...))))

相关问题