How are template expressions evaluated and converted to text? Walk through toString() invocation, null, and custom types.
answer
- template = evaluate then toString()
- null -> literal "null", no NPE
- data class gives readable toString()
- plain class gives Class@hash
- arrays print [I@hash — use joinToString
basics
~10 sKotlin runs the expression, then calls its toString() to get text. A null becomes the word "null". For your own classes, the result depends on the toString() you write (or the default one).
solid answer
~40 sEach template expression is evaluated at runtime, and the result is coerced to `String` by calling `toString()` on it. For nullable values, Kotlin emits the literal `"null"` rather than throwing — it effectively does a null-safe `toString()`. For a custom class, the output is whatever that class's `toString()` returns: by default that's `ClassName@hashHex` (e.g. `User@1b6d3586`), but a `data class` auto-generates a readable `toString()` like `User(name=Ada, age=30)`. Numbers use their `toString()` formatting, which can be locale-independent for primitives but matters for some types. Because templates always go through `toString()`, overriding `toString()` is the cleanest way to control how an object appears in logs and interpolated messages.
code
kotlin · 6 linesdata class User(val name: String, val age: Int)
val u: User? = null
println("$u") // null
println("${User("Ada", 30)}") // User(name=Ada, age=30)
println("${intArrayOf(1,2,3)}") // [I@... (trap)
println("${intArrayOf(1,2,3).joinToString()}") // 1, 2, 3go deeper
Knows the value is turned into text and null shows as "null".
Explains toString() invocation, data-class vs plain-class output, and null safety.
Calls out the array toString() trap and locale/formatting concerns, recommending overridden toString() or explicit formatting.
Sets conventions for domain toString() (no secrets/PII in logs) and distinguishes debug rendering from user-facing formatting.
## Evaluation order For `"a ${expr} b"`, Kotlin: 1. Emits the literal text `"a "`. 2. Evaluates `expr` (a full runtime expression — side effects included). 3. Converts the result to `String` via **`toString()`**. 4. Appends `" b"`. The compiler lowers all of this to `StringBuilder.append(...)` (or plain concatenation), so it's ordinary code, not reflection. ## null handling If the expression's static type is nullable and the value is `null`, the template emits the literal text `"null"`. It does **not** throw an NPE — the lowering uses a null-safe conversion equivalent to `String.valueOf`. So `"x=${maybeNull}"` yields `"x=null"` when the value is null. ```kotlin val s: String? = null println("value=$s") // value=null ``` ## Custom types and toString() The rendered text is **entirely determined by `toString()`**: ```kotlin class Plain(val id: Int) data class Point(val x: Int, val y: Int) println("${Plain(7)}") // Plain@<hash> (default Object-style toString) println("${Point(1, 2)}")// Point(x=1, y=2) (data class auto toString) ``` - A normal class inherits `Any.toString()`, producing `FQN@hexHashCode`. - A **`data class`** auto-generates a `toString()` listing its component properties. - Overriding `toString()` yourself gives full control: ```kotlin class Money(val cents: Int) { override fun toString() = "$%.2f".format(cents / 100.0) } println("Price: ${Money(1299)}") // Price: $12.99 ``` ## Collections and arrays Kotlin collections (`List`, `Set`, `Map`) have meaningful `toString()` (`[1, 2, 3]`, `{a=1}`). **Java arrays are the trap**: `IntArray`/`Array<T>` inherit the default object `toString()`, so `"${intArrayOf(1,2,3)}"` prints something like `[I@1b6d3586`. Use `.joinToString()` or `.contentToString()` instead. ## Practical guidance - Override `toString()` on domain types you log/interpolate. - Be aware that number `toString()` may differ from locale-aware formatting; for user-facing currency/percent formatting use `String.format`/`NumberFormat`, not raw interpolation. - For arrays, always convert explicitly.
- Why does interpolating an IntArray print garbage like [I@1b6d3586?Java arrays don't override toString(); they use Object's default. Use joinToString() or contentToString() for readable output.
- How do you make your own class render nicely in templates?Override toString() (or make it a data class), since templates always call toString() on the value.
saying these in an interview costs you the question
- Thinks null in a template throws an NPE
- Expects a plain class to print its fields without overriding toString()
- Doesn't know arrays print as [I@hash
- Believes interpolation does locale-aware number formatting automatically
- Says templates use the class name via reflection rather than toString()