如何将Kotlin的默认属性值设置为`this`

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

树根虽然让我头痛。 我想这样做,而不使用null所以我可以通过调用item.parent向上遍历树。 为了简化它,我希望根本身是一个父类,但是我不知道如何去做。

 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链。

你可以使用一个init块。 例如:

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

或者,你可以为“root”创建一个单独的“父”实现。 例如:

 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 

这样,你的“根”项目没有类似于你的“叶子”(“文件”)项目没有children属性的parent项属性。 你可能也想让你的ChildItemParentItem接口扩展一个通用的接口(例如命名的Item )。

@ 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可以帮助保护你,当你有可以为空的价值以及了解他们,而不是假装一切都OK。 然后,它给你很好的可操作性,以帮助你与他们合作。

这就是我将使用@ mfulton的答案:

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