Kotlin 语言参考文档 中文版 Help

可调试性

本章介绍关于可调试性需要注意的问题.

永远要提供 toString() 方法

为了便于调试, 要为你引入的每个类添加 toString() 方法的实现, 即使是对内部类也是如此. 如果 toString() 是契约(Contract) 的一部分, 那么要提供明确的文档说明.

下面的代码是图形建模代码简化后的例子:

class Vector2D(val x: Int, val y: Int) fun main() { val result = (1..20).map { Vector2D(it, it) } println(result) }

这段代码的输出没什么用处:

[Vector2D@27bc2616, Vector2D@3941a79c, Vector2D@506e1b77,...]

Debug Tool 窗口中提供的信息也没什么用处:

在 Debug Tool 窗口中 Vector 对象的输出

为了让日志和调试信息更加易于阅读, 请添加一个简单的 toString() 实现, 如下:

override fun toString(): String = "Vector2D(x=$x, y=$y)"

改善后的输出如下:

[Vector2D(x=1, y=1), Vector2D(x=2, y=2), Vector2D(x=3, y=3), ...
在 Debug Tool 窗口中 Vector 对象的改善后的输出

即使你认为这个类不会在任何地方打印输出, 也应该考虑实现 toString(), 因为它可能会以意想不到的方式提供帮助. 例如, 在 构建器 之内, 能够看到构建器目前的状态可能会非常重要.

class Person( val name: String?, val age: Int?, val children: List<Person> ) { override fun toString(): String = "Person(name=$name, age=$age, children=$children)" } class PersonBuilder { var name: String? = null var age: Int? = null val children = arrayListOf<Person>() fun child(personBuilder: PersonBuilder.() -> Unit = {}) { children.add(person(personBuilder)) } } fun person(personBuilder: PersonBuilder.() -> Unit = {}): Person { val builder = PersonBuilder() builder.personBuilder() return Person(builder.name, builder.age, builder.children) }

上面的代码预期的使用方式是:

Person DSL 和断点的使用方式

如果你在第一个 child 的右大括号之后的行设置断点 (如上图所示), 你会在 Debug Output 中看到一个无意义的字符串:

PersonBuilder 调试时的结果

如果你添加一个简单的 toString() 实现, 如下:

override fun toString(): String = "PersonBuilder(name=$name, age=$age, children=$children)"

调试信息会变得更加清晰:

PersonBuilder 改善后的调试结果

你还能立即看到哪些域变量已被设置, 哪些还没有设置.

下一步做什么?

学习 API 的 向后兼容性(Backward Compatibility).

最终更新: 2024/10/17