首页 > 解决方案 > 如何在不可变的 Kotlin 类中创建父子关系

问题描述

我有代表树的不可变类,其中孩子需要父引用。

sealed class Node {
    abstract val parent: Parent?
}

class Child(
    override val parent: Parent,
) : Node()

class Parent(
    override val parent: Parent?,
    val children: List<Node>
) : Node()

是否有一种惯用的 Kotlin 方法来创建具有正确设置父引用的子列表的父对象?

标签: kotlin

解决方案


您可以尝试使用根节点和构建器参数:

sealed class Node {
    data class Root(
        val createChildren: ParentList.() -> Unit
    ) : Node() {
        val children: List<Node> = ParentList(this).apply(createChildren)
    }

    data class Branch(
        val createChildren: ParentList.() -> Unit,
        val parent: Node
    ) : Node() {
        val children: List<Node> = ParentList(this).apply(createChildren)
    }

    data class Leaf(
        val parent: Node
    ) : Node()
}

class ParentList(
    val parent: Node,
    private val children: MutableList<Node> = mutableListOf()
) : List<Node> by children {

    fun branch(createChildren: ParentList.() -> Unit) {
        children += Node.Branch(createChildren, parent)
    }

    fun leaf() {
        children += Node.Leaf(parent)
    }

}

fun root(createChildren: ParentList.() -> Unit) = Node.Root(createChildren)

它们可以像下面这样构造(可能需要向任一节点添加额外的细节):

fun usage() {

    val graph = root {
        branch {
            leaf()
        }
        branch {
            branch {
                leaf()
            }
            leaf()
        }
    }
}

您可以允许访问具有扩展属性的潜在孩子和/或父母:

val Node.children: List<Node> get() = when(this) {
    is Node.Root -> children
    is Node.Branch -> children
    is Node.Leaf -> emptyList()
}

val Node.parent: Node? get() = when(this) {
    is Node.Root -> null
    is Node.Branch -> parent
    is Node.Leaf -> parent
}

所以你可以导航后代:

fun Node.allChildren(): List<Node> = 
  children + children.flatMap { it.allChildren() }

或向上导航:

fun Node.allParents(): List<Node> = 
  listOfNotNull(parent).flatMap { listOf(it) + allParents() }

为了避免在进行可能提前完成的搜索时进行评估,您始终可以使用序列而不是列表:

fun Node.allChildren(): Sequence<Node> = 
  children.asSequence() + children.asSequence().flatMap { it.allChildren() }

fun Node.allParents(): Sequence<Node> = 
  listOfNotNull(parent).asSequence().flatMap { sequenceOf(it) + it.allParents() }

注意:注意 Stackoverflows


推荐阅读