Skip to content
teach

Kotlin Resources

Knowledge

  • Docs: "Null safety", Kotlin
    Official docs for Kotlin's nullable/non-nullable type distinction, the safe-call and Elvis operators, and the platform types a Java interop boundary introduces. Use for: the primary mechanism behind writing null safety into the type system instead of into defensive checks.
  • Docs: "Types overview", Kotlin
    Official docs explaining that Kotlin's basic types (numbers, characters, booleans) behave like regular classes despite an optimized primitive representation at runtime. Use for: exactly why Int, Boolean, and similar types have member functions, unlike Java's primitives.
  • Docs: "Strings", Kotlin
    Official docs on Kotlin's immutable String type and string templates ($variable, ${expression}). Use for: string interpolation as a language feature, not string concatenation with extra syntax.
  • Docs: "Calling Java from Kotlin", Kotlin
    Official docs on the Java-to-Kotlin direction: platform types, the T! and Array<(out) T>! notation that appears only in diagnostics, and the two documented remedies (an explicit type annotation at the use site, or nullability annotations at the source). Also synthetic properties from Java getters and setters, with the detail that a setter-only class yields no property because Kotlin has none, and backtick escaping for Java identifiers that are Kotlin keywords. Use for: the boundary where null safety is lost, and how to restore it.
  • Docs: "Calling Kotlin from Java", Kotlin
    Official docs on the Kotlin-to-Java direction: top-level declarations compiling into a file class such as org.example.AppKt with @file:JvmName and @file:JvmMultifileClass to control it, @JvmOverloads for default parameters (and the restriction that it cannot be used on abstract or interface methods), @JvmField with its four conditions, and @Throws for a checked exception a Java caller has to catch. Use for: deciding what a Kotlin library owes its Java consumers.
  • Docs: "Generics: in, out, where", Kotlin
    Official docs on type parameters and variance, opening with why Java needs wildcards (its generic types are invariant, which is what stops an Integer reaching a list of strings) and stating plainly that Kotlin has none, offering declaration-site variance and type projections instead. Covers the out rule and its in mirror, use-site projections for a type like Array that both produces and consumes, star projections and what each declaration form makes them mean, captured types and why reads use the upper bound while writes use the lower one, and upper bounds with where. Use for: writing a signature that says what a caller may do with it, and for reading a projection diagnostic.
  • Docs: "Kotlin coroutines on Android", Android Developers
    Official guide to coroutines on Android, built around main-safety: a function is main-safe when it does not block UI updates on the main thread, and the guide's shape puts the withContext(Dispatchers.IO) inside the repository function so no caller has to remember. Also recommends injecting Dispatchers into the repository layer for easier testing, which is the same conclusion kotlinx-coroutines-test reaches from the test side. Use for: the Android half of the mission, and for where the wrapping of blocking work belongs.
  • Docs: "Use Kotlin coroutines with lifecycle-aware components", Android Developers
    Official docs on the scopes the platform owns for you: viewModelScope, cancelled automatically when the ViewModel is cleared (and replaceable by a constructor-injected scope so a test can pass a TestScope), LaunchedEffect for composition-scoped work, and collectAsStateWithLifecycle, which collects from STARTED to STOPPED by default with minActiveState to change it. Includes the warning that LaunchedEffect follows the composition rather than the Lifecycle, so it can run off-screen. Use for: choosing which lifetime a piece of work should follow.
  • Docs: "Backend development with Kotlin", Kotlin
    Official orientation page for server-side Kotlin: that it keeps full compatibility with existing Java-based technology stacks, and that a large Java codebase can be migrated gradually rather than at once. Use for: context before choosing a framework, and for the interoperability argument when a team is deciding whether to start.
  • Docs: "Gradle Kotlin DSL Primer", Gradle
    Official primer for build.gradle.kts: which type-safe model accessors exist in which kind of script, what to do when they are not available (configure<T>() and the<T>()), and ./gradlew kotlinDslAccessorsReport for discovering what an applied plugin actually contributes. Use for: writing a Kotlin build script without guessing, and for the fallback when an accessor is missing.
  • Docs: "Version Catalogs", Gradle
    Official docs on gradle/libs.versions.toml, auto-imported as libs: the alias-to-accessor mapping where each dash becomes a dot, registering further catalogs from the settings script, the TOML format, and the limitation that from may be called only once per catalog. Use for: keeping every version in one place, and for naming aliases so the generated accessors group sensibly.
  • Docs: "Graph Resolution", Gradle
    Official docs on how Gradle builds the dependency graph and resolves conflicts: it considers every requested version and by default selects the highest, prefers versions without qualifiers by comparing base versions first, and treats a same-capability clash as a separate kind of conflict resolved during variant selection. Use for: predicting which version reaches the classpath, and for the contrast with Maven's nearest-wins rule recorded in the Java workspace's testing and build sheet.
  • Docs: "The Java Library Plugin", Gradle
    Official docs on the api and implementation split: api dependencies are transitively exposed and appear on a consumer's compile classpath, implementation dependencies are not and do not leak into it. Use for: deciding which configuration a dependency belongs in, which is a statement about your library's surface rather than a formality.
  • Docs: "Creating Dependency Configurations", Gradle
    Official docs on configurations by role, declarable, resolvable and consumable, with the full set each Java plugin adds, the note that a configuration is intended for a single role, and how to create a custom one. Use for: reading an unfamiliar build file, and for unlearning the assumption that a configuration is a renamed Maven scope.
  • Site: MockK, mocking library for Kotlin
    The project site, which doubles as the reference: mockk, every and verify, the co forms for suspending functions (coEvery, coVerify, coJustAwait), mockkObject for singletons and its scoped block form, mockkStatic for top-level and module-wide extension functions, spyk, relaxed mocks and their trouble with generic return types, and the JUnit 5 extension that unmocks everything afterwards. Use for: doubling the Kotlin constructs a subclass-based mocking library cannot reach, and for the blast radius of each technique.
  • Docs: "Inheritance", Kotlin
    Official docs on open, and on the rule underneath a great deal of Kotlin testing advice: classes and their members are final by default and cannot be inherited from or overridden without it. Also notes that an overriding member is itself open unless marked final. Use for: why mocking works differently here, and for the argument against marking a class open to satisfy a test.
  • API: "kotlinx-coroutines-test", kotlinx.coroutines
    Package documentation for the coroutine test utilities: runTest and the TestScope it builds, the TestCoroutineScheduler as the shared source of virtual time, the difference between StandardTestDispatcher and UnconfinedTestDispatcher (which enters top-level children eagerly), Dispatchers.setMain, and the section that matters most, that virtual time is not used inside Dispatchers.IO, Default or Main, so those delays are real unless the dispatcher is replaceable. Use for: testing suspending code, and for the argument that a hardcoded dispatcher is a testability defect.
  • API: "kotlin-test", Kotlin
    Reference for the standard library's test façade: annotations and assertions that are independent of the test framework, the Asserter abstraction they delegate to, and the artifact list (kotlin-test-junit, kotlin-test-junit5, kotlin-test-testng, kotlin-test-js) that decides which framework the annotations are mapped onto. Use for: understanding why the same @Test behaves differently depending on a dependency.
  • Turbine, Cash App
    The testing library for Flow that the Kotlin flow documentation itself recommends. Covers flow.test { } with awaitItem(), awaitComplete() and awaitError(), testIn for several flows at once, the reason a Turbine test hangs rather than fails (testIn cannot clean up its own coroutine, needing runTest's backgroundScope or an explicit termination), cancelAndIgnoreRemainingEvents() for a flow that never completes on its own, ensureAllEventsConsumed(), expectMostRecentItem(), and the library's own documented dependency on an unstable kotlinx-coroutines-test API (UnconfinedTestDispatcher internals) to integrate with runTest. Use for: asserting what a flow emitted, in order, including one that runs forever.
  • Docs: "Introduction", Kotest
    Entry point for Kotest, the Kotlin-first alternative that replaces both the assertion layer and the runner: tests written in one of several styles (such as a class extending FunSpec with test("...") { } blocks) and infix matchers like shouldBe. Use for: deciding whether to adopt it wholesale, which is the only sensible way to adopt it.
  • Docs: "Cancellation and timeouts", Kotlin
    Official docs on cancellation through the Job handle: why it is cooperative, what a suspension point is and why a suspend call is not always one, the yield(), ensureActive() and isActive checks, prompt cancellation (a cancelled coroutine resumes with an exception rather than an available value), withContext(NonCancellable) for cleanup that must finish, and withTimeoutOrNull(). Use for: making long-running code actually stop, and for cleanup that survives cancellation. Note that cancellation-and-timeouts.html redirects here.
  • Docs: "Coroutine exceptions handling", Kotlin
    Official docs on the two builder flavours (launch propagates automatically, async exposes the exception through await), and on CoroutineExceptionHandler: that it belongs on a root coroutine, that children delegate to their parent so a handler on a child never runs, and that you cannot recover in it because the coroutine has already completed. Use for: working out where an exception from a coroutine actually surfaces.
  • Docs: "Flows", Kotlin
    Official docs on Flow: the emitter, intermediate operator and collector roles with the upstream and downstream vocabulary, cold flows (lazy, one execution per collector) against hot flows (SharedFlow and StateFlow, whose collectors are subscribers), the rule that a flow() builder must emit from its own coroutine context, .flowOn() as a context-preserving upstream switch, channelFlow() with its buffered channel for emitting from several coroutines, and the catch and retry operators. Use for: streaming many values over time instead of returning one, and for deciding between a cold and a hot flow. Note that flow.html redirects here.
  • Docs: "Coroutine context and dispatchers", Kotlin
    Official docs on the CoroutineContext as a set of elements, the dispatchers and what each confines execution to, withContext for switching and the extra dispatches a dispatcher change costs, context inheritance by children, and the two ways the parent-child relation gets overridden. Use for: reading which threads a coroutine runs on, and for why passing a Job into a builder detaches it from its scope.
  • API: "Dispatchers", kotlinx.coroutines
    Reference for the four standard dispatchers, including the exact wording of what Main is confined to and what Unconfined does with the initial continuation. Use for: choosing a dispatcher, and for the fact that Default is what every builder falls back to.
  • API: "Dispatchers.IO", kotlinx.coroutines
    Reference for the blocking-IO dispatcher: the parallelism property and its default of 64 threads or the core count, the elasticity of limitedParallelism views (with a worked MySQL and MongoDB example), and two facts that correct the obvious model, that IO shares threads with Default so switching to it often does not change thread, and that its limit bounds blocking tasks rather than threads. Use for: sizing and partitioning blocking work in a service.
  • API: "CoroutineScope", kotlinx.coroutines
    Reference with a "Structured concurrency in detail" section: that a coroutine cannot reach its final state until all its children have, that cancelling a scope cancels every child, the exact conditions under which a child's failure fails its parent, and the CoroutineScope() constructor function for a scope tied to an entity's lifetime (with the warning that cancelling it is the caller's job). Use for: deciding between a lexical scope and an owned one, and for the precise propagation rules.
  • API: "GlobalScope", kotlinx.coroutines
    Reference for the @DelicateCoroutinesApi global scope, with the pitfalls stated as the library sees them (computations running when they are no longer needed or have no right to) and the narrow legitimate case of a process that must live as long as the application. Use for: arguing about a GlobalScope.launch in review, in the library authors' own terms.
  • Docs: "Coroutines basics", Kotlin
    Official docs on the suspend keyword and the rule that a suspending function can only be called from another one, the three coroutine builders with what each returns, and a direct comparison of a coroutine against a JVM thread (stack size, how many of each a process holds, what happens to the thread when a coroutine suspends). Use for: the entry point to the concurrency stage, and for why delay and Thread.sleep are not interchangeable.
  • Docs: "Composing suspending functions", Kotlin
    Official docs on combining suspending calls: sequential by default, async and await for genuine concurrency with the elapsed times measured, lazily started async via CoroutineStart.LAZY, and the difference between Job and Deferred. Use for: making two independent slow operations concurrent, and for seeing that suspend alone never does it for you.
  • Spec: "Threads and Locks", Java Language Specification, Java SE 21
    Chapter 17 of the JLS: the memory model itself, happens-before, and the final field semantics whose freeze action happens when a constructor exits. Use for: the model Kotlin on the JVM actually runs under, since the language defines no memory model of its own on this platform. Dense; the Java workspace's concurrency sheet is the lookup-shaped companion to it.
  • API: "Volatile", Kotlin
    Reference for the @Volatile annotation, including the distinction the name hides: operations on the annotated backing field are atomic, while a property operation through a custom accessor that touches the field several times is not. Use for: what @Volatile guarantees, and the read-modify-write case it does not cover.
  • API: "Synchronized", Kotlin
    Reference for @Synchronized: which monitor the generated JVM method takes, why the annotation is wrong on an extension function (the facade class's monitor, not the receiver's), and the deprecation of the common declaration, an error since Kotlin 2.1. Use for: choosing between the annotation and the synchronized(lock) { } function.
  • API: "LazyThreadSafetyMode", Kotlin
    Reference for the three modes lazy takes: SYNCHRONIZED (the default, one initialising thread and a visible result), PUBLICATION (the initializer may run more than once, one value wins), and NONE (no locks, unspecified across threads). Use for: why by lazy is thread-safe without being asked, and what the opt-outs actually cost.
  • API: "thread", Kotlin
    Reference for the standard library's thread function, which creates and by default starts a java.lang.Thread, with isDaemon, name and priority as named arguments. Use for: starting a platform thread from Kotlin without the Thread subclass or Runnable ceremony.
  • Docs: "Aggregate operations", Kotlin
    Official docs on the operations that reduce a collection to a single value: count, sum, average, the min and max family with its OrNull and By variants, and fold/reduce with the initial-value difference between them spelled out on a worked example. Use for: choosing between a named aggregate and a hand-written fold, and for why the same lambda gives different answers to fold and reduce.
  • Docs: "Grouping", Kotlin
    Official docs on groupBy(), which materialises a Map of key to member list, and groupingBy(), which returns a Grouping that operations such as eachCount() consume without building those lists. Use for: the difference that decides whether a per-group aggregation allocates in proportion to the input.
  • Docs: "Sequences", Kotlin
    Official docs on Sequence<T>: lazy multistep processing, the intermediate/terminal operation split, the four ways to construct one, stateless versus stateful operations, and the explicit warning that laziness has an overhead of its own. Use for: deciding between a collection pipeline and a sequence, and for the element-by-element execution order that lets a bound like take cut the work short.
  • Docs: "Collection operations overview", Kotlin
    Official docs on how collection operations are declared: essential behaviour as member functions of the collection interfaces, everything else as extension functions, none of them touching the receiver. Use for: why map and filter are not members of List, and why an operation whose result nobody keeps still does all its work.
  • Docs: "Collection transformation operations", Kotlin
    Official docs on map, mapNotNull, zip, associate, flatten/flatMap and joinToString, each building a new collection from an existing one. Use for: the transformation half of a collection pipeline, and the fact that every step in a chain materialises a collection of its own.
  • Docs: "Filtering collections", Kotlin
    Official docs on filter and its variants, partition, and the any/none/all predicate tests, including all() returning true on an empty collection by vacuous truth. Use for: narrowing a collection, and the empty-input edge case a validation guard written with all gets wrong.
  • Docs: "Operator overloading", Kotlin
    Official docs on the fixed set of operator symbols (+, *, [], comparisons, and more) and the exact function name and operator modifier each maps to. Use for: implementing an operator only where it means what the symbol already means to a reader, not as a way to write terse but surprising code.
  • Docs: "Delegation", Kotlin
    Official docs on class delegation via by: implementing an interface by forwarding to a held object, with zero boilerplate, and the subtlety that overrides in the derived class aren't seen by the delegate's own internal calls. Use for: the delegation pattern as a language feature instead of hand-written forwarding methods.
  • Docs: "Delegated properties", Kotlin
    Official docs on property delegation (by lazy { ... } and custom delegates via getValue()/setValue()). Use for: reusable property behavior (lazy initialization, change observation) without repeating the same accessor logic on every property that needs it.
  • Docs: "Inline functions", Kotlin
    Official docs on the inline modifier's actual cost/benefit trade-off, non-local returns, noinline/crossinline, and reified type parameters, which only work because inlining erases the usual generics-erasure boundary. Use for: why inline and reified are paired, not two unrelated features.
  • Docs: "Higher-order functions and lambdas", Kotlin
    Official docs on function types, lambda syntax, trailing-lambda convention, and function literals with receiver (A.(B) -> C), the mechanism scope functions like run and apply are built on. Use for: functions as real values, not just something a lambda is loosely shorthand for.
  • Docs: "Type-safe builders", Kotlin
    Official docs on building a type-safe builder DSL from a receiver-style function type, the receiver-conflict problem a nested builder introduces (a closure carries access to every enclosing receiver, not just the nearest one), and @DslMarker as the fix, restricting implicit member access to the nearest receiver unless an outer one is named explicitly. Use for: the construct receiver-style function types actually exist for.
  • Docs: "Scope functions", Kotlin
    Official docs on let, run, with, apply, and also: the object reference each provides (it vs this), what each returns (the lambda's result vs the context object), and the function-selection table distinguishing them. Use for: choosing the right scope function by what it actually returns, not by habit or resemblance to another one.
  • Docs: "Extensions", Kotlin
    Official docs on extension functions and properties: called as if they were members, but resolved statically and never actually modifying the extended class or interface. Use for: adding behavior to a type you don't own (including one from a library) without inheritance or a wrapper class.
  • Docs: "Interfaces", Kotlin
    Official docs on interfaces with default method bodies, abstract versus implemented properties (and why interfaces can't hold backing-field state), and resolving a diamond conflict with super<Type>.member(). Use for: modelling shared behavior across unrelated types without inheritance.
  • Docs: "Object declarations and expressions", Kotlin
    Official docs on object declarations (thread-safe, lazily-initialized singletons), companion objects, and object expressions for anonymous, one-time instances. Use for: what replaces Java's static members and singleton-pattern boilerplate.
  • Docs: "Enum classes", Kotlin
    Official docs on enum classes: each constant as a real object, optional per-constant anonymous class bodies, and the entries/valueOf()/name/ordinal machinery every enum gets for free. Use for: what an enum actually is beyond a list of names, and how it differs from a sealed class covering the same kind of fixed set.
  • Docs: "Sealed classes and interfaces", Kotlin
    Official docs on sealed hierarchies: every direct subclass known at compile time, and the compiler-enforced exhaustiveness this gives a when expression over one, with no else branch needed. Use for: type-safe state modelling where an exception or a runtime check would otherwise stand in for the type system.
  • Docs: "Data classes", Kotlin
    Official docs on what data class generates automatically (equals()/hashCode(), toString(), componentN(), copy()), its requirements, and the subtle rule that only primary-constructor properties participate. Use for: what a data class actually buys over a hand-written class, precisely.
  • Docs: "Inline value classes", Kotlin
    Official docs on @JvmInline value class: the single-property constraint, the compiler's preference for an unboxed runtime representation, the documented "boxed whenever used as another type" rule (generics, Any, interfaces), and the doubly-nullable case that boxes even ordinary-looking usage. Use for: the zero-cost fix for primitive obsession, and precisely where "zero-cost" stops applying.
  • Repo: kotlinx.serialization, Kotlin
    Official repo and docs for the compiler-plugin-based, reflectionless serialization library: the two-piece setup (compiler plugin plus runtime dependency), full JVM/JS/Native multiplatform support, and the explicit, per-type serialization-strategy philosophy with no global configuration point. Use for: what a Java habit's reflective serializer (Jackson, Gson) doesn't do, and why it can't reach every Kotlin target.
  • API: "runCatching", Kotlin
    Official stdlib reference for runCatching's Result<T>-returning contract, and that it catches any Throwable a block throws, including CancellationException. Use for: the exact stdlib contract that collides with coroutine cancellation if used unguarded.
  • Docs: "Routing", Ktor
    Official docs for Ktor's routing DSL: the per-verb functions (get, post, put, and others), path patterns including named parameters and full Regex support, and route for grouping and nesting. Use for: routing as an application of lesson 36's type-safe builder mechanism, not a new one.
  • Docs: "Server plugins", Ktor
    Official docs stating that routing itself is implemented as a plugin, that Ktor activates none by default, and that a plugin can be scoped to specific routes rather than only installed globally. Use for: why nothing in a Ktor application works until it's explicitly installed.
  • Docs: "Handling requests", Ktor
    Official docs for extracting request data (requirePathParameter, requireQueryParameter, requireCookie), each throwing (MissingRequestParameterException) rather than returning null when a required value is actually absent. Use for: the boundary code lesson 31's "nullability lives at the edge" principle describes, applied concretely.
  • Docs: "Configuration in a file", Ktor
    Official docs for application.conf/application.yaml, environment-variable substitution (${ENV}, and the optional ${?ENV} form for overriding a prior default), custom configuration sections alongside Ktor's own reserved block, and command-line config-file overrides. Use for: the exact idiom for a value with a safe default and an environment-driven override.
  • Docs: "Logging in Ktor Server", Ktor
    Official docs on SLF4J as Ktor's JVM logging abstraction, and its silent no-op fallback when no concrete logging framework is present. Use for: why logging calls that compile and run can still produce zero output.
  • Docs: "Call logging", Ktor
    Official docs for the CallLogging plugin: default Level.INFO, the filter { } block for logging only some requests, and mdc(...) for per-request diagnostic values scoped to one call's lifetime. Use for: request-level logging, and the pattern-update step MDC values need to actually appear in output.
  • Docs: "Working with Transactions", Exposed
    Official JetBrains docs for Exposed's transaction model: synchronous, blocking execution on the current thread by default, newSuspendedTransaction/suspendedTransactionAsync as the coroutine-friendly alternative (always starting a fresh transaction), and nested-transaction rollback scoped by SQL SAVEPOINT. Use for: why a plain Exposed transaction inside a suspending function is a real defect, and the documented fix.
  • Docs: "Integrate a database with Kotlin, Ktor, and Exposed", Ktor
    Official docs for the withTransaction() pattern: a suspending block run inside a new top-level transaction with the coroutine context switched to Dispatchers.IO. Use for: dispatching Exposed's underlying blocking JDBC work correctly instead of stalling the handler's own thread.
  • Docs: "Classes", Kotlin
    Official docs on Kotlin classes: the primary constructor, properties declared directly in the class header, and when the docs themselves recommend a data class or an extension function instead of a plain class. Use for: what a class actually needs to encapsulate, before reaching for one reflexively.
  • Docs: "Properties", Kotlin
    Official docs on Kotlin properties: backing fields, custom getters and setters, and why properties replace the getter/setter boilerplate a Java class needs by hand. Use for: what a property actually compiles to, and where a custom accessor is worth writing.
  • Docs: "Conditions and loops", Kotlin
    Official docs on if as an expression, when and its exhaustiveness, and Kotlin's for loop over ranges and collections. Use for: control flow as expressions producing values, not just statements.
  • Docs: "Ranges and progressions", Kotlin
    Official docs on Kotlin's range operators (.., ..<) and the progressions a for loop actually iterates over. Use for: what a range literally is, before treating for (i in 1..10) as unexplained syntax.
  • Docs: "Collections overview", Kotlin
    Official docs on List/Set/Map, and the read-only/mutable interface pair behind each, including why a mutable collection held by a val is still mutable. Use for: the collection-level counterpart to lesson 2's reference-vs-object mutability distinction.
  • Docs: "Equality", Kotlin
    Official docs distinguishing structural equality (==, calls equals()) from referential equality (===, same object identity), and which Kotlin types override equals() by default. Use for: exactly what == means in Kotlin, since it is not Java's ==.
  • Docs: "Basic syntax overview", Kotlin
    Official overview of Kotlin's core syntax elements, including val/var, basic types, and control flow, each linked to its own detailed page. Use for: the language's basic building blocks, before idiom (stage 3) asks for more than syntax.
  • Docs: "Coroutines guide", Kotlin
    Official guide to coroutines: suspending functions, structured concurrency, and dispatchers. Use for: how Kotlin's concurrency model actually works, before comparing it to anything else.
  • JEP 444: "Virtual Threads", OpenJDK
    The official specification for Java 21's virtual threads: what problem they solve and how they're scheduled under the hood. Use for: the specific comparison point the mission names, coroutines against Java 21 virtual threads.
  • Docs: "Kotlin for Java developers", Kotlin
    Official docs naming, directly, what's different (and what's deliberately similar) between Kotlin and Java. Use for: locating exactly where a Java habit stops applying.
  • Docs: "Coding conventions", Kotlin
    The official style guide for idiomatic Kotlin: naming, formatting, and idioms the language expects, as opposed to code that merely compiles. Use for: recognizing Kotlin written with a Java accent versus Kotlin written idiomatically.
  • Site: "Kotlin on Android", Android Developers
    Official entry point for Kotlin's Android-specific idioms and libraries (coroutines with lifecycle-aware scopes, Android KTX). Use for: the Android half of this mission's coverage, where it diverges from a backend/server context.

Gaps

  • No source contrasting Kotlin coroutines against Java 21 virtual threads side by side. Lesson 27 needed that comparison and built it from two sources instead: the Kotlin coroutine docs listed above for the coroutine side, and the Java workspace's concurrency sheet for the virtual-thread side, whose figures are measured on one machine rather than quoted. The gap that remains is a single authoritative treatment; the two-source version is sound but its axes are this repository's own, not a citable framing.
  • Dispatchers.IO.limitedParallelism numbers, and the coroutine equivalents of the concurrency sheet's measured ratios, come from no measurement of this workspace's own. Lesson 25 states the documented limits and lesson 27 attributes the ratios to the Java workspace's runs; a Kotlin-side benchmark would let both stop borrowing.
Table of contents