Checkpoint: Idiomatic Kotlin

This checkpoint consolidates the advanced idioms you've learned — annotations, operator overloading, value classes, variance, DSLs, Result, advanced collections, testing, and Java interop — into one build challenge and a self-check quiz.

Learn Checkpoint: Idiomatic Kotlin in our free Kotlin course — a beginner-friendly interactive lesson with worked examples, a practice exercise and a quick…

Part of the free Kotlin course at LearnCodingFast — hands-on lessons with examples you run in your browser, plus practice exercises and a quick quiz.

Work the challenge first, then reveal the solution, then test yourself with the quiz before moving on to the capstone.

What This Checkpoint Covers

🔁 Quick Recap

🛠️ Build Challenge: A Typed Receipt

Bring three idioms together: a value class for money, an overloaded + operator, and a collection pipeline that folds a total and groups names by category. Work it yourself first, then reveal the solution.

Notice how the value class keeps Cents distinct from a bare Int , the plus operator makes acc + item.price read naturally, and the two-argument groupBy maps each category straight to a list of names.

⏱ Timed Quiz

📝 Checkpoint Quiz

Answer each in your head, then expand to check. No peeking first!

It must wrap exactly one property, and that property must be a val . On the JVM it also needs the @JvmInline annotation. For multiple fields, use a data class instead.

The function plus backs + , and it must be marked with the operator keyword. A single compareTo (returning an Int ) similarly backs all four of < > <= >= .

MutableList both produces T (reads) and consumes T (adds), so neither in nor out is safe — it must stay invariant. Read-only List only produces, so it's declared out E and a List<Dog> is a List<Animal> .

groupBy returns Map<K, List<V>> , keeping every element that shares a key. associateBy returns Map<K, V> with one value per key, so on a collision the last element wins.

Use runCatching for expected , recoverable failures you want to handle as a value rather than a thrown exception. fold(onSuccess, onFailure) collapses the Result into a single value by handling both branches at once.

@JvmOverloads generates extra overloads so Java callers can omit Kotlin default arguments (Java has no defaults). Java getX()/setX(v) pairs appear in Kotlin as the property x , used with obj.x and obj.x = v .

Practice quiz

What is the one structural restriction on a value class?

  • It must have at least two fields
  • It cannot have methods
  • It must wrap exactly one val property
  • It must be abstract

Answer: It must wrap exactly one val property. A value class wraps exactly one val property and, on the JVM, needs the @JvmInline annotation.

Which function name backs the + operator, and what keyword must it carry?

  • plus, with the operator keyword
  • add, with the override keyword
  • combine, with the infix keyword
  • sum, with the inline keyword

Answer: plus, with the operator keyword. The function plus backs +, and it must be marked with the operator keyword.

Why is MutableList<T> invariant while List<T> is covariant?

  • MutableList only reads T
  • List both reads and writes T
  • Invariance is just the default and means nothing
  • MutableList both produces and consumes T, so neither in nor out is safe

Answer: MutableList both produces and consumes T, so neither in nor out is safe. MutableList both produces (reads) and consumes (adds) T, so it must stay invariant; read-only List only produces, so it is out E and covariant.

What does groupBy return compared to associateBy?

  • Both return Map<K, V>
  • groupBy returns Map<K, List<V>>; associateBy returns Map<K, V>
  • groupBy returns a List; associateBy returns a Set
  • Both return Map<K, List<V>>

Answer: groupBy returns Map<K, List<V>>; associateBy returns Map<K, V>. groupBy keeps every element sharing a key as Map<K, List<V>>, while associateBy keeps one value per key (last wins) as Map<K, V>.

When does runCatching beat a try/catch?

  • For expected, recoverable failures you want to handle as a value
  • Never; try/catch is always better
  • Only for syntax errors
  • Only inside coroutines

Answer: For expected, recoverable failures you want to handle as a value. runCatching captures success/failure as a Result value, ideal for expected failures you transform with map/fold instead of throwing.

What does fold(onSuccess, onFailure) do to a Result?

  • It ignores the failure branch
  • It throws if the Result failed
  • It collapses the Result into a single value by handling both branches
  • It converts Result to a List

Answer: It collapses the Result into a single value by handling both branches. Result.fold collapses both the success and failure branches into one value at once.

What does @JvmOverloads solve?

  • It makes a class final
  • It generates overloads so Java callers can omit Kotlin default arguments
  • It marks a field static
  • It renames methods for Java

Answer: It generates overloads so Java callers can omit Kotlin default arguments. Java has no default arguments, so @JvmOverloads generates extra overloads letting Java callers omit them.

How does a Java getX()/setX(v) pair appear when used from Kotlin?

  • As getX() and setX() method calls
  • It is hidden from Kotlin
  • As a function pair x() and x(v)
  • As the property x, used with obj.x and obj.x = v

Answer: As the property x, used with obj.x and obj.x = v. Kotlin maps Java getter/setter pairs to a property, accessed as obj.x and assigned with obj.x = v.

What powers a Kotlin DSL builder like apply or buildString?

  • A reflection call
  • A lambda with receiver, e.g. T.() -> Unit
  • An annotation processor
  • A sealed class

Answer: A lambda with receiver, e.g. T.() -> Unit. DSL builders use a lambda with receiver (T.() -> Unit) so the lambda body can call the receiver's members directly.

Which variance keyword marks a type parameter that is only produced (returned)?

  • in
  • reified
  • out
  • vararg

Answer: out. out marks a covariant producer position; in marks a contravariant consumer position.