Kotlin Help

Debuggability

This chapter contains considerations about debuggability.

Always provide a toString() method

To make debugging easier, add a toString() implementation to every class you introduce, even to internal ones. If toString() is part of a contract, document it explicitly.

The following code is a simplified example from a graphical modeling area:

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

The output of this code is not very useful:

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

Neither is the information provided in the debug tool window:

Vector class objects in the debug tool window

To make both logging and debugging much more readable, add a simple toString() implementation like this:

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

This results in improved output:

[Vector2D(x=1, y=1), Vector2D(x=2, y=2), Vector2D(x=3, y=3), ...
Improved output of vector class objects in the debug tool window

Consider implementing toString() even if you don't think the class is going to be printed anywhere, as it can help in unexpected ways. For example, inside builders, it may be important to see the current state of the builder.

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) }

The intended use of the code above is the following:

Usage of the person DSL and a breakpoint

If you set a breakpoint on the line after the closing brace of the first child (as on the picture above), you see a non-descriptive string in debug output:

Result of a PersonBuilder debugging

If you add a simple toString() implementation like this:

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

The debug data becomes much clearer:

Result of a PersonBuilder improved debugging

You can also see immediately which fields are set and which are not.

What's next?

Learn about APIs' backward compatibility.

Last modified: 12 April 2024