如何将Kotlin默认属性值设置为“this”

时间:2016-09-23 15:03:43

标签: kotlin

我有以下类结构,代表简单的树。每个项目可以有多个子项和父项。

树根让我头疼。我试图在不使用null的情况下执行此操作,因此我可以通过调用item.parent来向上遍历树。为了简化它,我希望root将自己作为父级,但我无法弄清楚如何做到这一点。

interface Item {
    val parent: Directory
}

interface ItemWithChildren{
    val children: MutableList<Item>
}

class Directory() : Item, ItemWithChildren {
    override val children: MutableList<Item> = mutableListOf()
    override val parent: Directory by lazy { this }

    constructor(par: Directory) : this() {
        parent = par //Error: val cannot be reassigned 
    }
}

class File(override val parent: Directory) : Item

该代码无法编译,因为无法重新分配val parent。但是使用this作为默认参数值也是不可能的。有什么出路吗?

如果我允许父级可以为空,那么解决方案很简单。但如果可能的话,我不想使用空值。此外null会击败item.parent链。

3 个答案:

答案 0 :(得分:3)

您可以使用init块。 e.g:

class Directory(parent: Directory? = null) : Item, ItemWithChildren {
    override val children: MutableList<Item> = mutableListOf()
    override val parent: Directory

    init {
        this.parent = parent ?: this
    }
}

或者,您可以为“root”创建单独的“父”实现。 e.g:

interface ChildItem /* renamed from `Item` for clarity */ {
    val parent: ParentItem
}

interface ParentItem /* renamed from `ItemWithChildren` for clarity */ {
    val children: MutableList<ChildItem>
}

class Root() : ParentItem {
    override val children: MutableList<ChildItem> = mutableListOf()
}

class Directory(override val parent: ParentItem) : ChildItem, ParentItem {
    override val children: MutableList<ChildItem> = mutableListOf()
}

class File(override val parent: ParentItem) : ChildItem

这样,您的“root”项目没有parent属性,类似于“leaf”(“file”)项目没有children属性的方式。您可能还希望使ChildItemParentItem接口扩展一个公共接口(例如,名为Item)。

答案 1 :(得分:1)

@ mfulton26以严格要求的方式回答了如何做到这一点。但对于其他可能对这种选择感到疑惑的人,他们仍然应该考虑在Kotlin中这类工作的null值。

您可以拥有null属性和一些派生属性,这些属性允许将访问权限声明为null。因为无论哪种方式(您计划避免null或接受和使用null),您都不得不问“我有父母吗?”这几乎是一样的问“是父null吗?”那么为什么一个不常见的“可能无限循环导致”解决这种情况呢?

如果我的树类类似于:

data class Something(val text: String, val parentOrNull: Something? = null) {
    val parent: Something get() = parentOrNull!!
    val hasParent = parentOrNull != null
}

然后,我可以选择如何访问父级,无需担心null

val root = Something("rooty")
val child = Something("youngun", root)
val leaf = Something("baby", child)

fun printPathToNode(node: Something) {
    // use derived properties to check and not worry about the null
    if (node.hasParent) printPathToNode(node.parent)
    println(node)
}

fun findRoot(node: Something): Something {
    // use null operators to not worry about the null
    return node.parentOrNull?.let { findRoot(it) } ?: node
}

然后你可以看到它运行良好且输出良好,null没有问题:

printPathToNode(leaf)    // rooty, youngun, baby
printPathToNode(child)   // rooty, youngun
printPathToNode(root)    // rooty

println(findRoot(leaf))  // rooty
println(findRoot(child)) // rooty
println(findRoot(root))  // rooty

在没有意义的情况下应该避免无效。但有时它们实际上是一个合理的选择。 Kotlin有助于保护你,当你有可空的价值时,通过了解它们而不只是假装一切都没问题。然后它会给你很好的nullability operators来帮助你与他们合作。

答案 2 :(得分:1)

我将如何使用@ mfulton的答案:

class Directory(parent: Directory? = null) : Item, ItemWithChildren {
    override val children = mutableListOf<Item>()
    override val parent = parent ?: this
}