【问题标题】:How to set Kotlin default property value to `this`如何将 Kotlin 默认属性值设置为 `this`
【发布时间】:2016-09-24 04:03:09
【问题描述】:

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

树根让我头疼。我试图在不使用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 链。

【问题讨论】:

    标签: kotlin


    【解决方案1】:

    您可以使用init 块。例如:

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

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

    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
    

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

    【讨论】:

    • 谢谢,root的单独实现其实是我需要的。这将使我能够进一步简化我的代码。
    【解决方案2】:

    @mfulton26 回答了如何按照您严格要求的方式执行此操作。但是对于可能对这种选择感到疑惑的其他人,他们仍然应该考虑null 值对于 Kotlin 中的此类工作来说是可以的。

    您可以拥有一个null 属性和一些允许访问断言为非null 的派生属性。因为无论哪种方式(您计划避免null 或接受并使用null),您都将不得不问“我有父母吗?”这几乎与问“is parent 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 来帮助你与他们合作。

    【讨论】:

    • 感谢您的洞察力!坚决拒绝零是不正确的心态。我被 Kotlin 标准库中不存在的可选数据类型的想法“吸引”了。但就我而言,这实际上不会完成比 null 本身更有用的任何事情。
    【解决方案3】:

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

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

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2010-10-16
      • 2010-11-18
      • 1970-01-01
      • 1970-01-01
      • 2023-03-15
      • 1970-01-01
      • 1970-01-01
      • 2013-07-09
      相关资源
      最近更新 更多