Kotlin Professional Certificate common mistakes
Kotlin Professional Certificate · Understand the traps and how to avoid them.
Writing Java-style code in the coding tasks: declaring explicit types everywhere, using `for (i in 0 until list.size)` loops, and mutating `var` accumulators instead of using `map`/`filter`/`fold`.
Candidates with a Java background default to imperative idioms under time pressure and never show the idiomatic Kotlin the coding tasks are scored on.
What to do instead
Before the exam, rewrite 20 small exercises twice: once imperatively, once with collection operations and expression bodies. Practise val-first style and single-expression functions until they are the default reflex.
Confusing `==` with `===`, and assuming `==` compares references for custom classes.
Kotlin maps == to equals, but candidates coming from other languages expect identity comparison, and they forget that data classes generate equals/hashCode while regular classes do not.
What to do instead
Memorise the rule: == calls equals, === compares references. Check whether a class is a data class before assuming value equality, and override equals/hashCode (or use a data class) whenever instances go into sets or map keys.
Mishandling nullability: using `!!` to silence the compiler, or misjudging what `?.`, `?:`, and `let` return when the receiver is null.
Candidates reach for !! as the fastest way to make code compile, and they misread the resulting type of a safe-call chain (for example expecting a non-null value from a?.b).
What to do instead
Replace every !! with ?: with a sensible default, an early return, or requireNotNull. Practise predicting the static type of x?.y?.z, x?.let { ... }, and x ?: y before compiling, then verify with the IDE's inferred-type hint.
Mixing up `val` and `var`, and assuming `val` makes an object immutable.
val only freezes the reference; a val list backed by a mutable list can still change, and candidates answer immutability questions as if val guaranteed deep immutability.
What to do instead
State the rule explicitly: val = read-only reference, not immutable object. When asked about immutability, check the concrete type (List vs MutableList) and whether the underlying collection is exposed.
Getting inheritance and interface semantics wrong: forgetting `open`, misusing `override`, or being surprised that interfaces can hold state-free default implementations but not backing fields.
Kotlin classes and members are final by default, unlike Java, and candidates carry over Java assumptions about abstract classes versus interfaces.
What to do instead
Drill the modifiers: open, abstract, override, final. Remember interfaces may declare abstract members and provide default bodies but cannot store state; abstract classes can. Write a small hierarchy using both and note which members need open.
Misusing scope functions and lambdas: choosing `apply` where `also` fits, losing the return value of `run`/`let`, or forgetting that `it` is the implicit parameter unless renamed.
let, run, with, apply, and also differ only in receiver versus argument and in return value, and candidates memorise names without the two-axis table.
What to do instead
Build the 2x2 table (context object as it or this; returns lambda result or receiver) and place each function in it. Then rewrite five snippets using the correct one and predict the return type before compiling.
Treating `List`, `MutableList`, `Set`, and `Map` as interchangeable, or expecting `map`/`filter` results to be mutable.
Candidates assume listOf(...) returns something addable, or that map preserves the original collection type, and they misjudge ordering and duplicate semantics of sets.
What to do instead
Remember the read-only interfaces are views: listOf and the result of map/filter are not MutableList. Use toMutableList() when mutation is required, and check whether ordering or uniqueness matters before picking a collection.
Writing coroutine code that blocks: using `Thread.sleep`, `runBlocking` inside a suspend function, or a dispatcher that serialises work, and misunderstanding structured concurrency so a failing child does not cancel siblings.
Candidates know suspend syntax but not the cancellation and dispatcher model, so they translate blocking patterns directly and are surprised by leaked or uncancelled jobs.
What to do instead
Use delay instead of Thread.sleep, and coroutineScope/supervisorScope deliberately. Practise with Dispatchers.Default for CPU work and Dispatchers.IO for blocking calls, and verify cancellation propagates by cancelling a parent and observing children.
Writing tests that assert on implementation details or forget that JUnit assertions need the expected value first, and never running the specific failing test in isolation.
Candidates write tests that pass locally by accident, or invert assertEquals(expected, actual), and they rely on a full build instead of the IDE's single-test runner, wasting time.
What to do instead
Always write assertEquals(expected, actual) and test observable behaviour through the public API. Before the exam, practise running one test, one class, and one package from the IDE gutter, and reading the failure diff to locate the assertion.
Ignoring compiler warnings and IDE inspections, then submitting code that compiles but fails review on unused variables, redundant null checks, or shadowed names.
Candidates treat green compilation as done and dismiss warnings as cosmetic, but professional-level tasks are graded on idiomatic, warning-free code.
What to do instead
Enable "Treat warnings as errors" while practising and fix every inspection the IDE flags. Learn the quick-fix shortcuts (Alt+Enter) for redundant modifiers, unused imports, and simplifiable expressions, and run the analyzer before finishing each task.