Kotlin essentials: 50 cards on null safety, data classes and coroutines
Kotlin fundamentals with short snippets: null safety, val vs var, data and sealed classes, extension functions, scope functions and coroutines.
Practice now
Study without an account
String vs String? in KotlinScheduled with the default Askesis timings. Progress is saved in this browser.
All 50 cards
- 1
StringvsString?in KotlinStringcan never holdnull;String?can. The compiler won’t let you assignnullto aString, or call methods on aString?without handling the null case - 2What does
user?.address?.cityevaluate to ifaddressis null?null: a safe call?.short-circuits tonullinstead of throwing - 3The Elvis operator
?:Returns the left side if it isn’t null, otherwise the right sidekotlinval name = input ?: "anonymous" val user = findUser(id) ?: return // early exit - 4What does
!!do?Asserts that a value isn’t null and throwsNullPointerExceptionif it is. Treat every!!as a possible crash - 5What does this print?kotlin
val s: String? = null println(s?.length ?: -1)-1:s?.lengthisnull, so the Elvis operator supplies-1 - 6What is a smart cast?After a check like
if (s != null)orif (x is String), the compiler treats the value as the narrower type, with no explicit castIt doesn’t work on a mutablevarproperty, because the value could change between the check and the use. - 7Run a block only when a value isn’t nullkotlin
email?.let { sendWelcome(it) } - 8Cast without risking a
ClassCastExceptionkotlinval n = value as? Int ?: 0as?returnsnullwhen the cast fails; plainasthrows. - 9What are platform types like
String!?Types coming from Java code without nullability annotations. Kotlin lets you treat them as nullable or not, so a wrong guess can throwNullPointerExceptionat runtime - 10
valvsvarvalis a read-only reference that can’t be reassigned;varcan be. Prefervalvaldoesn’t make the object immutable:val xs = mutableListOf(1)can stilladd. - 11
valvsconst valconst valis a compile-time constant: a top-level orobjectmember of a primitive type orString. A plainvalcan be computed at runtime - 12String templateskotlin
println("Hello, $name! You have ${cart.size} items")Use${…}for anything beyond a simple name. - 13Write a ternary in Kotlinkotlin
val max = if (a > b) a else bKotlin has nocond ? a : boperator;ifandwhenare expressions that return values. - 14A
whenexpressionkotlinval size = when (n) { 0 -> "none" 1, 2 -> "few" in 3..9 -> "some" else -> "many" } - 15Ranges:
1..5,1..<5,5 downTo 11..5includes 5;1..<5(or1 until 5) stops before 5;5 downTo 1counts down. Addstep 2to skip valuesThe..<operator is stable since Kotlin 1.9. - 16
==vs===in Kotlin==compares structurally by callingequals(and handles null);===checks whether both references point to the same object - 17Default and named argumentskotlin
fun greet(name: String, greeting: String = "Hello") = println("$greeting, $name") greet("Ada") greet(greeting = "Hi", name = "Ada")They replace most overloads and builder patterns. - 18A single-expression functionkotlin
fun square(x: Int) = x * xThe return type (Int) is inferred from the expression. - 19
UnitvsNothingUnitis the return type of functions with no useful result (likevoid, but a real object);Nothingis the type of expressions that never complete, likethrowandTODO() - 20What does
data class User(val name: String, val age: Int)generate?equals,hashCode,toString,copy, andcomponent1()andcomponent2()for destructuring, all based on the primary constructor’s properties - 21What does this print?kotlin
data class User(val name: String, val age: Int) println(User("Ada", 36))User(name=Ada, age=36)A regular class would print something likeUser@1b6d3586. - 22Copy a data class with one property changedkotlin
val older = user.copy(age = 37)copyis shallow: nested objects are shared, not cloned. - 23Destructure a data classkotlin
val (name, age) = userDestructuring is positional: it usescomponent1(),component2()… in constructor order, not property names. - 24Can you subclass any Kotlin class?No. Classes and members are
finalby default: mark the classopento allow subclassing, and each methodopento allow overriding it withoverride - 25Declare properties directly in the constructorkotlin
class Person(val name: String, var age: Int) { init { require(age >= 0) { "age must be >= 0" } } }valorvarin the primary constructor creates a property;initblocks run as part of construction. - 26Kotlin’s replacement for
staticmembersAnobjectdeclaration is a singleton; acompanion objectinside a class holds members you call on the class, likeUser.create()kotlinclass User private constructor(val name: String) { companion object { fun create(name: String) = User(name.trim()) } } - 27Model a closed set of results with a sealed interfacekotlin
sealed interface Result data class Success(val data: String) : Result data class Failure(val error: Throwable) : Result fun render(r: Result) = when (r) { is Success -> r.data is Failure -> "Error: ${r.error.message}" }The compiler knows every subtype, sowhenneeds noelseand flags missing cases. - 28Sealed class vs enum classEach enum constant is a single instance; a sealed type’s subclasses are ordinary classes that can have many instances, each with its own state
- 29
lateinit varvsby lazylateinit varis a non-null property you assign later (reading it first throws);val x by lazy { … }computes its value on first access and caches itlateinitdoesn’t work with primitive types likeInt;lazyis thread-safe by default. - 30What does
internalvisibility mean?Visible everywhere in the same module, such as one Gradle module, but not outside itKotlin’s default visibility ispublic. - 31Add a function to
Stringwithout subclassing itkotlinfun String.shout() = uppercase() + "!" println("hi".shout()) // HI!Inside an extension function,thisis the receiver. - 32Are extension functions dispatched virtually?No. They are resolved statically from the declared type, can’t access private members, and lose to a member function with the same signature
- 33
listOfvsmutableListOflistOfreturns a read-onlyList(noadd);mutableListOfreturns aMutableListRead-only isn’t immutable: aListreference can point to a list that other code mutates. - 34Get the sorted names of all adultskotlin
val names = users .filter { it.age >= 18 } .map { it.name } .sorted()itis the implicit name of a lambda’s single parameter. - 35What is trailing lambda syntax?When a function’s last parameter is a function, the lambda can go outside the parentheses, and the parentheses can be dropped if it is the only argumentkotlin
repeat(3) { println("hi") } names.forEach { println(it) } - 36Group users by citykotlin
val byCity: Map<String, List<User>> = users.groupBy { it.city }associateBy { it.id }builds a map with one value per key instead. - 37When should you use
asSequence()?For long chains over large collections: sequences process elements lazily, one at a time, instead of building an intermediate list after every stepkotlinval firstBig = numbers.asSequence() .map { it * it } .first { it > 1_000 } - 38What does this print?kotlin
fun firstEven(xs: List<Int>): Int { xs.forEach { if (it % 2 == 0) return it } return -1 } println(firstEven(listOf(1, 4, 6)))4: a barereturninside the lambda of an inline function likeforEachreturns from the enclosing functionUsereturn@forEachto skip to the next element instead. - 39The five scope functions at a glance
let(it, returns the lambda result),run(this, lambda result),with(this, lambda result, not an extension),apply(this, returns the object),also(it, returns the object) - 40Which scope function configures an object and returns it?kotlin
val user = User().apply { name = "Ada" age = 36 }applyruns the block with the object asthisand returns the object itself. - 41Which scope function adds a side effect, like logging, in a chain?kotlin
val user = repository.save(newUser) .also { log.info("saved ${it.id}") }alsoreceives the object asitand returns it unchanged. - 42What are
aandb?kotlinval a = "hello".let { it.length } val b = "hello".also { it.length }ais5, becauseletreturns the lambda’s result;bis"hello", becausealsoreturns the receiver - 43What is a
suspendfunction?A function that can pause without blocking its thread, e.g. while waiting for I/O, and resume later. It can only be called from a coroutine or anothersuspendfunction - 44
launchvsasynclaunchstarts a coroutine for its side effects and returns aJob;asyncstarts one that produces a value and returns aDeferred<T>, read withawait() - 45Fetch two things concurrently and combine themkotlin
suspend fun loadPage() = coroutineScope { val user = async { api.fetchUser() } val posts = async { api.fetchPosts() } Page(user.await(), posts.await()) } - 46What is structured concurrency?Every coroutine runs inside a scope that waits for all its children; cancelling the scope cancels them, and a failing child cancels its siblings and parent, so nothing leaksA
supervisorScopelets children fail independently. - 47Which dispatcher for which work?
Dispatchers.Mainfor UI,Dispatchers.IOfor blocking I/O,Dispatchers.Defaultfor CPU-heavy work. Switch withwithContextkotlinval text = withContext(Dispatchers.IO) { file.readText() } - 48
delay()vsThread.sleep()delaysuspends the coroutine and frees the thread for other work;Thread.sleepblocks the whole thread - 49When is
runBlockingappropriate?Only to bridge blocking code into coroutines, such as inmainor tests. It blocks the current thread, so never use it inside coroutines or on a UI thread - 50What is a
Flow?A cold asynchronous stream: theflow { … }block runs again for each collector, sending values withemitthat are received withcollectkotlinflow { for (i in 1..3) { delay(100); emit(i) } } .collect { println(it) }
Frequently asked questions
Which Kotlin version does the deck use?
Current Kotlin 2.x. Nearly everything also applies to late 1.x releases; newer additions like the ..< range operator (1.9) are marked.
Do I need to know Java first?
No. A few cards compare Kotlin with Java, such as static members and platform types, but they make sense on their own.
Does it cover Android or Jetpack Compose?
No. It covers the language and the kotlinx.coroutines basics that Android, Compose and server-side Kotlin all build on.
More in Programming
JavaScript
Master the JavaScript every developer uses daily: scope, closures, equality, array methods, promises, async/await and the event loop.
50 cardsAdvanced JS
Fifty advanced JavaScript cards: event-loop ordering puzzles, generators and async iteration, this binding, Proxy and Reflect, modules, AbortController and coercion edge cases.
50 cardsTypeScript
Learn the TypeScript you use every day: narrowing, generics, keyof, mapped and conditional types, utility types, satisfies and strict mode, with code on every card.
50 cardsPython
Learn idiomatic Python fast: comprehensions, f-strings, args and kwargs, generators, decorators, context managers and the standard library.
50 cardsGit & terminal
The Git commands and shell skills developers use every day: branching, rebasing, undoing mistakes, pipes, redirects, permissions and processes.
50 cardsJava
Core Java with short snippets: equals vs ==, records, sealed types, collections, generics, exceptions, streams, lambdas and JVM basics.
50 cardsC#
C# and .NET fundamentals with short snippets: value vs reference types, properties, records, nullable reference types, LINQ, async/await and DI.
50 cardsSwift
Swift fundamentals with short snippets: optionals, structs vs classes, protocols, enums with associated values, closures, ARC, async/await and SwiftUI.
50 cardsFlutter
Flutter and Dart basics: null safety, futures and streams, stateless vs stateful widgets, setState, hot reload, keys, layout and state management.
50 cardsPHP
Modern PHP 8 with short snippets: strict types, arrays, match, nullsafe and named arguments, enums, readonly, Composer, PDO and password_hash.
50 cardsRuby
Ruby fundamentals with short snippets: blocks, procs and lambdas, symbols, Enumerable, mixins, truthiness, method_missing, gems and Bundler.
50 cardsGo
Go (Golang) fundamentals with short, idiomatic snippets: slices vs arrays, maps, defer, error wrapping, interfaces, goroutines, channels, select and context.
50 cardsRust
Learn the Rust that the compiler keeps asking about: ownership, borrowing, lifetimes, Option and Result, traits, closures, smart pointers and async.
50 cardsC++
Modern C++ in short snippets: references vs pointers, RAII, smart pointers, move semantics, STL containers and algorithms, const, templates and UB.
50 cardsHaskell
Haskell fundamentals with short code: types and type classes, pattern matching, laziness, Maybe and Either, folds, Functor, Applicative and Monad.
50 cardsElixir
Elixir fundamentals with short snippets: immutability, pattern matching, the pipe operator, Enum vs Stream, processes, GenServer, supervisors and mix.
50 cardsx86-64 assembly
x86-64 assembly in Intel syntax: registers and sub-registers, flags, mov and lea, jumps, the stack frame, the System V calling convention and Linux syscalls.
50 cardsRegex
Learn regular expressions for good: anchors, classes, greedy vs lazy quantifiers, groups, lookarounds, flags, replacements and the backtracking trap.
50 cards