Moving from Java to Kotlin is frequently framed as "learn the new syntax," which gets it backwards. The syntax is the easy part. The actual migration is unlearning the habits Java forced on you — the getters and setters, the null checks everywhere, the anonymous inner classes — and learning to trust a language that lets you write the intent in one line instead of thirty. Every Java → Kotlin pairing on the cheat sheet is really the same lesson: Kotlin took a pattern you typed out of habit and made it a language feature.
Null-safety is the headline feature
The single biggest, and the one that genuinely changes how you design. In Java, `null` is a silent landmine — any reference can be null, so you either check everywhere or crash sometimes. Kotlin makes null an opt-in: a type is non-null by default, and only a type marked `String?` can hold null. The compiler then forces you to handle nullability before you can use the value.
- `val` vs `var` — read-only vs mutable, and read-only is the default you should reach for
- `?` on a type — declares nullability explicitly; the compiler now knows what to check
- `?.` safe call, `?:` Elvis operator — the two operators that replace entire `if (x != null)` forests
- `!!` — the explicit "I know better" that throws if it's null; treat every `!!` in a codebase as a suspect, because it's a NPE you chose
- The pay-off: whole classes of NPE crashes become impossible to write, not merely "caught in review"
The migrations that actually matter
Not every Java construct has a dramatic Kotlin equivalent — but the ones that do are worth learning first, because they're where the productivity comes from:
- A full POJO (getters, setters, equals, hashCode, toString) → a `data class` on one line. This is the single biggest win in the language
- `switch` → `when`, which is an expression that returns a value and needs no `break`
- Static utility methods → extension functions, so you can add `fun Int.triple()` and call `3.triple()` instead of `Utils.triple(3)`
- `instanceof` + cast → `is` + smart cast, where the compiler already knows the type inside the block
- Anonymous inner classes → object expressions / lambdas; boilerplate ceremony collapses into `it ->`
- The ternary `? :` → `if` as an expression, or the Elvis `?:` for the null-default case
The mindset shift, not the syntax shift
The real transition isn't learning that `data class` exists — it's learning to *expect* that the thing you're about to type 30 lines of has a one-line form. New Kotlin developers translate Java line-by-line and produce Kotlin that looks like Java with shorter keywords; the moment they get it is when they start reaching for the construct that expresses the intent, and the boilerplate disappears on its own. The language is opinionated on purpose: it wants you to write less, and to write code where the bugs you'd normally write can't be written.
In this note
Prefer it hands-on?
This note has a matching interactive topic with diagrams and a runnable repo.