Kotlin中toString的递归问题

时间:2017-08-31 00:39:05

标签: kotlin

我用Kotlin编写XML DSL并遇到了问题。代码:

abstract class Element(val name: String) {

    var children = mutableListOf<Element>()

    override fun toString() = """
        <$name>
            ${children.joinToString("\n") { toString() }}
        </$name>
    """.trimIndent()

}

我在{ toString() }上有以下错误:

  

类型检查遇到了递归问题。最简单的解决方法:明确指定声明的类型。

我需要以下输出:

<a>
    <b>
    </b>
    <c>
    </c>
</a>

如果我有以下代码:

fun main(args: Array<String>) {
    val a = Element("a")
    a.children.add(Element("b"))
    a.children.add(Element("c"))
    println(a)
}

我该如何解决这个问题?

2 个答案:

答案 0 :(得分:1)

当您从toString函数的lambda参数调用joinToString时,您没有指定toString的接收者。在这种情况下,使用范围中的隐式接收器thisthis指向父元素而不是当前子元素,因此您正在进行递归调用。

在lambda中你应该使用隐式参数名it访问子元素,或者明确命名参数。

children.joinToString("\n") { it.toString() }
children.joinToString("\n") { child -> child.toString() }

然而,这不会使递归类型检查问题消失,因为这里在其主体内引用相同的toString,其类型尚未被推断。要打破这种递归,您需要明确指定toString的返回类型。

override fun toString(): String = ...

答案 1 :(得分:1)

我是Kotlin的新手,所以这可能不是非常惯用的。但它确实有用。

class Element(val name: String) {

    var children = mutableListOf<Element>()

    private fun recursiveToString(depth: Int): String {
        fun tabulations(amount: Int) = "\t".repeat(amount)
        val childrenAsString: String = children.joinToString("") {
            tabulations(depth + 1) + it.recursiveToString(depth + 1) 
        }
        return "<$name>\n$childrenAsString${tabulations(depth)}</$name>\n"
}

    override fun toString() = recursiveToString(0)
}

fun main(args: Array<String>) {
    val a = Element("a")
    a.children.add(Element("b"))
    val c = Element("c")
    c.children.add(Element("d"))
    a.children.add(c)
    println(a)
}