liveBook · Manning

What you will learn in Kotlin in Action, 2E

by Sebastian Aigner,Svetlana Isakova,Roman Elizarov

Chapter by chapter

Chapter 1: Kotlin: What and why

The chapter introduction characterizes Kotlin as a pragmatic successor to Java and shows how its uses have expanded across applications and platforms.

1.1 A taste of Kotlin · 1.2 Kotlin’s primary traits · 1.2.1 Kotlin use cases: Android, server side, anywhere Java runs, and beyond · 1.2.2 Static typing makes Kotlin performant, reliable, and maintainable · 1.2.3 Combining functional and object-oriented programming makes Kotlin safe and flexible · 1.2.4 Concurrent and asynchronous code becomes natural and structured with coroutines · 1.2.5 Kotlin can be used for any purpose: It’s free, open source, and open to contributions · 1.3 Areas in which Kotlin is often used · 1.3.1 Powering backends: Server-side development with Kotlin · 1.3.2 Mobile Development: Android is Kotlin first · 1.3.3 Multiplatform: Sharing business logic and minimizing duplicate work on iOS, JVM, JS, and beyond · 1.4 The philosophy of Kotlin · 1.4.1 Kotlin is a pragmatic language · 1.4.2 Kotlin is concise · 1.4.3 Kotlin is safe · 1.4.4 Kotlin is interoperable · 1.5 Using the Kotlin tools · 1.5.1 Setting up and running the Kotlin code · 1.5.2 Compiling Kotlin code · 1.6 Summary

What you learn in chapter 1 →

Chapter 2: Kotlin basics

This chapter introduction previews Kotlin basics and explains what it means to write idiomatic Kotlin.

2.1 Basic elements: Functions and variables · 2.1.1 Writing your first Kotlin program: “Hello, world!” · 2.1.2 Declaring functions with parameters and return values · 2.1.3 Making function definitions more concise by using expression bodies · 2.1.4 Declaring variables to store data · 2.1.5 Marking a variable as read only or reassignable · 2.1.6 Easier string formatting: String templates · 2.2 Encapsulating behavior and data: Classes and properties · 2.2.1 Associating data with a class and making it accessible: Properties · 2.2.2 Computing properties instead of storing their values: Custom accessors · 2.2.3 Kotlin source code layout: Directories and packages · 2.3 Representing and handling choices: Enums and when · 2.3.1 Declaring enum classes and enum constants · 2.3.2 Using the when expression to deal with enum classes · 2.3.3 Capturing the subject of a when expression in a variable · 2.3.4 Using the when expression with arbitrary objects · 2.3.5 Using the when expression without an argument · 2.3.6 Smart casts: Combining type checks and casts · 2.3.7 Refactoring: Replacing an if with a when expression · 2.3.8 Blocks as branches of if and when · 2.4 Iterating over things: while and for loops · 2.4.1 Repeating code while a condition is true: The while loop · 2.4.2 Iterating over numbers: Ranges and progressions · 2.4.3 Iterating over maps · 2.4.4 Using in to check collection and range membership · 2.5 Throwing and catching exceptions in Kotlin · 2.5.1 Handling exceptions and recovering from errors: try, catch, and finally · 2.5.2 Using try as an expression · 2.6 Summary

What you learn in chapter 2 →

Chapter 3: Defining and calling functions

Introduces the chapter’s focus on making Kotlin functions easier to declare and call, and on adapting Java libraries to Kotlin style.

3.1 Creating collections in Kotlin · 3.2 Making functions easier to call · 3.2.1 Named arguments · 3.2.2 Default parameter values · 3.2.3 Getting rid of static utility classes: Top-level functions and properties · 3.3 Adding methods to other people’s classes: Extension functions and properties · 3.3.1 Imports and extension functions · 3.3.2 Calling extension functions from Java · 3.3.3 Utility functions as extensions · 3.3.4 No overriding for extension functions · 3.3.5 Extension properties · 3.4 Working with collections: varargs, infix calls, and library support · 3.4.1 Extending the Java collections API · 3.4.2 Varargs: Functions that accept an arbitrary number of arguments · 3.4.3 Working with pairs: Infix calls and destructuring declarations · 3.5 Working with strings and regular expressions · 3.5.1 Splitting strings · 3.5.2 Regular expressions and triple-quoted strings · 3.5.3 Multiline triple-quoted strings · 3.6 Making your code tidy: Local functions and extensions · 3.7 Summary

What you learn in chapter 3 →

Chapter 4: Classes, objects, and interfaces

Introduces concrete differences in Kotlin class declarations and the language features that make classes more flexible and concise.

4.1 Defining class hierarchies · 4.1.1 Interfaces in Kotlin · 4.1.2 Open, final, and abstract modifiers: Final by default · 4.1.3 Visibility modifiers: Public by default · 4.1.4 Inner and nested classes: Nested by default · 4.1.5 Sealed classes: Defining restricted class hierarchies · 4.2 Declaring a class with nontrivial constructors or properties · 4.2.1 Initializing classes: Primary constructor and initializer blocks · 4.2.2 Secondary constructors: Initializing the superclass in different ways · 4.2.3 Implementing properties declared in interfaces · 4.2.4 Accessing a backing field from a getter or setter · 4.2.5 Changing accessor visibility · 4.3 Compiler-generated methods: Data classes and class delegation · 4.3.1 Universal object methods · 4.3.2 Data classes: Autogenerated implementations of universal methods · 4.3.3 Class delegation: Using the by keyword · 4.4 The object keyword: Declaring a class and creating an instance, combined · 4.4.1 Object declarations: Singletons made easy · 4.4.2 Companion objects: A place for factory methods and static members · 4.4.3 Companion objects as regular objects · 4.4.4 Object expressions: Anonymous inner classes rephrased · 4.5 Extra type safety without overhead: Inline classes · 4.6 Summary

What you learn in chapter 4 →

Chapter 5: Programming with lambdas

The chapter introduces lambdas as passable chunks of code that help make Kotlin code reusable and expressive.

5.1 Lambda expressions and member references · 5.1.1 Introduction to lambdas: Blocks of code as values · 5.1.2 Lambdas and collections · 5.1.3 Syntax for lambda expressions · 5.1.4 Accessing variables in scope · 5.1.5 Member references · 5.1.6 Bound callable references · 5.2 Using Java functional interfaces: Single abstract methods · 5.2.1 Passing a lambda as a parameter to a Java method · 5.2.2 SAM constructors: Explicit conversion of lambdas to functional interfaces · 5.3 Defining SAM interfaces in Kotlin: fun interfaces · 5.4 Lambdas with receivers: with, apply, and also · 5.4.1 Performing multiple operations on the same object: with · 5.4.2 Initializing and configuring objects: The apply function · 5.4.3 Performing additional actions with an object: also · 5.5 Summary

What you learn in chapter 5 →

Chapter 6: Working with collections and sequences

Introduces functional collection processing as a common use of lambdas and previews sequences as an alternative for applying collection operations.

6.1 Functional APIs for collections · 6.1.1 Removing and transforming elements: filter and map · 6.1.2 Accumulating values for collections: reduce and fold · 6.1.3 Applying a predicate to a collection: all, any, none, count, and find · 6.1.4 Splitting a list into a pair of lists: partition · 6.1.5 Converting a list to a map of groups: groupBy · 6.1.6 Transforming collections into maps: associate, associateWith, and associateBy · 6.1.7 Replacing elements in mutable collections: replaceAll and fill · 6.1.8 Handling special cases for collections: ifEmpty · 6.1.9 Splitting collections: chunked and windowed · 6.1.10 Merging collections: zip · 6.1.11 Processing elements in nested collections: flatMap and flatten · 6.2 Lazy collection operations: Sequences · 6.2.1 Executing sequence operations: Intermediate and terminal operations · 6.2.2 Creating sequences · 6.3 Summary

What you learn in chapter 6 →

Chapter 7: Working with nullable values

Introduces nullable types as a Kotlin feature that improves code reliability and previews the chapter’s treatment of null values and Java interoperability.

7.1 Avoiding NullPointerExceptions and handling the absence of values: Nullability · 7.2 Making possibly null variables explicit with nullable types · 7.3 Taking a closer look at the meaning of types · 7.4 Combining null checks and method calls with the safe call operator: ?. · 7.5 Providing default values in null cases with the Elvis operator: ?: · 7.6 Safely casting values without throwing exceptions: as? · 7.7 Making promises to the compiler with the non-null assertion operator: !! · 7.8 Dealing with nullable expressions: The let function · 7.9 Non-null types without immediate initialization: Late-initialized properties · 7.10 Extending types without the safe-call operator: Extensions for nullable types · 7.11 Nullability of type parameters · 7.12 Nullability and Java · 7.12.1 Platform types · 7.12.2 Inheritance · 7.13 Summary

What you learn in chapter 7 →

Chapter 8: Basic types, collections, and arrays

Introduces the reliability-oriented choices in Kotlin’s type system that shape its basic types and collections.

8.1 Primitive and other basic types · 8.1.1 Representing integers, floating-point numbers, characters, and Booleans with primitive types · 8.1.2 Using the full bit range to represent positive numbers: Unsigned number types · 8.1.3 Nullable primitive types: Int?, Boolean?, and more · 8.1.4 Kotlin makes number conversions explicit · 8.1.5 Any and Any?: The root of the Kotlin type hierarchy · 8.1.6 The Unit type: Kotlin’s void · 8.1.7 The Nothing type: “This function never returns” · 8.2 Collections and arrays · 8.2.1 Collections of nullable values and nullable collections · 8.2.2 Read-only and mutable collections · 8.2.3 Kotlin collections and Java collections are deeply related · 8.2.4 Collections declared in Java are seen as platform types in Kotlin · 8.2.5 Creating arrays of objects and primitive types for interoperability and performance reasons · 8.3 Summary

What you learn in chapter 8 →

Chapter 9: Operator overloading and other conventions

Introduces Kotlin conventions as language operations enabled by specially named functions and explains why extensions make those conventions useful for existing classes.

9.1 Overloading arithmetic operators makes operations for arbitrary classes more convenient · 9.1.1 Plus, times, divide, and more: Overloading binary arithmetic operations · 9.1.2 Applying an operation and immediately assigning its value: Overloading compound assignment operators · 9.1.3 Operators with only one operand: Overloading unary operators · 9.2 Overloading comparison operators makes it easy to check relationships between objects · 9.2.1 Equality operators: equals (==) · 9.2.2 Ordering operators: compareTo (<, >, ⇐, and >=) · 9.3 Conventions used for collections and ranges · 9.3.1 Accessing elements by index: The get and set conventions · 9.3.2 Checking whether an object belongs to a collection: The in convention · 9.3.3 Creating ranges from objects: The rangeTo and rangeUntil conventions · 9.3.4 Making it possible to loop over your types: The iterator convention · 9.4 Making destructuring declarations possible with component functions · 9.4.1 Destructuring declarations and loops · 9.4.2 Ignoring destructured values using the _ character · 9.5 Reusing property accessor logic: Delegated properties · 9.5.1 Basic syntax and inner workings of delegated properties · 9.5.2 Using delegated properties: Lazy initialization and by lazy() · 9.5.3 Implementing your own delegated properties · 9.5.4 Delegated properties are translated to hidden properties with custom accessors · 9.5.5 Accessing dynamic attributes by delegating to maps · 9.5.6 How a real-life framework might use delegated properties · 9.6 Summary

What you learn in chapter 9 →

Chapter 10: Higher-order functions:Lambdas as parameters and return values

Introduces higher-order functions as a way to use lambdas in your own abstractions, beyond collection and standard-library functions.

10.1 Declaring functions that return or receive other functions: Higher-order functions · 10.1.1 Function types specify the parameter types and return values of a lambda · 10.1.2 Calling functions passed as arguments · 10.1.3 Java lambdas are automatically converted to Kotlin function types · 10.1.4 Parameters with function types can provide defaults or be nullable · 10.1.5 Returning functions from functions · 10.1.6 Making code more reusable by reducing duplication with lambdas · 10.2 Removing the overhead of lambdas with inline functions · 10.2.1 Inlining means substituting a function body to each call site · 10.2.2 Restrictions on inline functions · 10.2.3 Inlining collection operations · 10.2.4 Deciding when to declare functions as inline · 10.2.5 Using inlined lambdas for resource management with withLock, use, and useLines · 10.3 Returning from lambdas: Control flow in higher-order functions · 10.3.1 Return statements in lambdas: returning from an enclosing function · 10.3.2 Returning from lambdas: Return with a label · 10.3.3 Anonymous functions: Local returns by default · 10.4 Summary

What you learn in chapter 10 →

Chapter 11: Generics

The chapter introduces two Kotlin generics concepts: reified type parameters and the distinction between declaration-site and use-site variance.

11.1 Creating types with type arguments: Generic type parameters · 11.1.1 Functions and properties that work with generic types · 11.1.2 Generic classes are declared with the angle bracket syntax · 11.1.3 Restricting the type a generic class or function can use: Type parameter constraints · 11.1.4 Excluding nullable type arguments by explicitly marking type parameters as non-null · 11.2 Generics at run time: Erased and reified type parameters · 11.2.1 Limitations to finding type information of a generic class at run time: Type checks and casts · 11.2.2 Functions with reified type parameters can refer to actual type arguments at run time · 11.2.3 Avoiding java.lang.Class parameters by replacing class references with reified type parameters · 11.2.4 Declaring accessors with reified type parameters · 11.2.5 Reified type parameters come with restrictions · 11.3 Variance describes the subtyping relationship between generic arguments · 11.3.1 Variance determines whether it is safe to pass an argument to a function · 11.3.2 Understanding the differences between classes, types, and subtypes · 11.3.3 Covariance preserves the subtyping relation · 11.3.4 Contravariance reverses the subtyping relation · 11.3.5 Specifying variance for type occurrences via use-site variance · 11.3.6 Star projection: Using the * character to indicate a lack of information about a generic argument · 11.3.7 Type aliases · 11.4 Summary

What you learn in chapter 11 →

Chapter 12: Annotations and reflection

Introduces annotations as metadata on declarations and reflection as a way to inspect arbitrary classes at run time, using a JSON library to illustrate both.

12.1 Declaring and applying annotations · 12.1.1 Applying annotations to mark declarations · 12.1.2 Specifying the exact declaration an annotation refers to: Annotation targets · 12.1.3 Using annotations to customize JSON serialization · 12.1.4 Creating your own annotation declarations · 12.1.5 Meta-annotations: Controlling how an annotation is processed · 12.1.6 Passing classes as annotation parameters to further control behavior · 12.1.7 Generic classes as annotation parameters · 12.2 Reflection: Introspecting Kotlin objects at run time · 12.2.1 The Kotlin reflection API: KClass, KCallable, KFunction, and KProperty · 12.2.2 Implementing object serialization using reflection · 12.2.3 Customizing serialization with annotations · 12.2.4 JSON parsing and object deserialization · 12.2.5 The final step of deserialization: callBy() and creating objects using reflection · 12.3 Summary

What you learn in chapter 12 →

Chapter 13: DSL construction

Introduces Kotlin DSL design and identifies lambdas with receivers as a way to give DSL code blocks their own functions, properties, and behavior.

13.1 From APIs to DSLs: Creating expressive custom code structures · 13.1.1 Domain-specific languages · 13.1.2 Internal DSLs are seamlessly integrated into the rest of your program · 13.1.3 The structure of DSLs · 13.1.4 Building HTML with an internal DSL · 13.2 Building structured APIs: Lambdas with receivers in DSLs · 13.2.1 Lambdas with receivers and extension function types · 13.2.2 Using lambdas with receivers in HTML builders · 13.2.3 Kotlin builders: Enabling abstraction and reuse · 13.3 More flexible block nesting with the invoke convention · 13.3.1 The invoke convention: Objects callable as functions · 13.3.2 The invoke convention in DSLs: Declaring dependencies in Gradle · 13.4 Kotlin DSLs in practice · 13.4.1 Chaining infix calls: The should function in test frameworks · 13.4.2 Defining extensions on primitive types: Handling dates · 13.4.3 Member extension functions: Internal DSL for SQL · 13.5 Summary

What you learn in chapter 13 →

Chapter 14: Coroutines

The introduction explains why applications need asynchronous work and identifies coroutines as Kotlin’s approach to it.

14.1 Concurrency vs. parallelism · 14.2 Concurrency the Kotlin way: Suspending functions and coroutines · 14.3 Comparing threads and coroutines · 14.4 Functions that can pause: Suspending functions · 14.4.1 Code written with suspending functions looks sequential · 14.5 Comparing coroutines to other approaches · 14.5.1 Calling a suspending function · 14.6 Entering the world of coroutines: Coroutine builders · 14.6.1 From regular code into the realm of coroutines: The runBlocking function · 14.6.2 Creating start-and-forget coroutines: The launch function · 14.6.3 Awaitable computations: The async builder · 14.7 Deciding where your code should run: Dispatchers · 14.7.1 Choosing a dispatcher · 14.7.2 Passing a dispatcher to a coroutine builder · 14.7.3 Using withContext to switch the dispatcher within a coroutine · 14.7.4 Coroutines and dispatchers aren’t a magical fix for thread-safety concerns · 14.8 Coroutines carry additional information in their coroutine context · 14.9 Summary

What you learn in chapter 14 →

Chapter 15: Structured concurrency

This introduction explains why applications need to track coroutine lifetimes and presents structured concurrency as Kotlin’s built-in way to do so.

15.1 Coroutine scopes establish structure between coroutines · 15.1.1 Creating a coroutine scope: The coroutineScope function · 15.1.2 Associating coroutine scopes with components: CoroutineScope · 15.1.3 The danger of GlobalScope · 15.1.4 Coroutine contexts and structured concurrency · 15.2 Cancellation · 15.2.1 Triggering cancellation · 15.2.2 Invoking cancellation automatically after a time limit has been exceeded · 15.2.3 Cancellation cascades through all children · 15.2.4 Cancelled coroutines throw CancellationExceptions in special places · 15.2.5 Cancellation is cooperative · 15.2.6 Checking whether a coroutine has been cancelled · 15.2.7 Letting other coroutines play: The yield function · 15.2.8 Keep cancellation in mind when acquiring resources · 15.2.9 Frameworks can perform cancellation for you · 15.3 Summary

What you learn in chapter 15 →

Chapter 16: Flows

Introduces flows as a coroutine-based way to work with sequential values over time.

16.1 Flows model sequential streams of values · 16.1.1 Flows allow you to work with elements as they are emitted · 16.1.2 Different types of flows in Kotlin · 16.2 Cold flows · 16.2.1 Creating a cold flow with the flow builder function · 16.2.2 Cold flows don’t do any work until collected · 16.2.3 Cancelling the collection of a flow · 16.2.4 Cold flows under the hood · 16.2.5 Concurrent flows with channel flows · 16.3 Hot flows · 16.3.1 Shared flows broadcast values to subscribers · 16.3.2 Keeping track of state in your system: State flow · 16.3.3 Comparing state flows and shared flows · 16.3.4 Hot, cold, shared, state: When to use which flow · 16.4 Summary

What you learn in chapter 16 →

Chapter 17: Flow operators

This section introduces flow operators and distinguishes operators that build a modified flow from those that collect and execute it.

17.1 Manipulating flows with flow operators · 17.2 Intermediate operators are applied to an upstream flow and return a downstream flow · 17.2.1 Emitting arbitrary values for each upstream element: The transform function · 17.2.2 The take operator family can cancel a flow · 17.2.3 Hooking into flow phases with onStart, onEach, onCompletion, and onEmpty · 17.2.4 Buffering elements for downstream operators and collectors: The buffer operator · 17.2.5 Throwing away intermediate values: The conflate operator · 17.2.6 Filtering out values on a timeout: The debounce operator · 17.2.7 Switching the coroutine context on which a flow is executed: The flowOn operator · 17.3 Creating custom intermediate operators · 17.4 Terminal operators execute the upstream flow and may compute a value · 17.4.1 Frameworks provide custom operators · 17.5 Summary

What you learn in chapter 17 →

Chapter 18: Error handling and testing

The introduction explains why robust concurrent Kotlin applications need graceful error handling and testing when components or external services fail.

18.1 Handling errors thrown inside coroutines · 18.2 Error propagation in Kotlin coroutines · 18.2.1 Coroutines cancel all their children when one child fails · 18.2.2 Structured concurrency only affects exceptions thrown across coroutine boundaries · 18.2.3 Supervisors prevent parents and siblings from being cancelled · 18.3 CoroutineExceptionHandler: The last resort for processing exceptions · 18.3.1 Differences when using CoroutineExceptionHandler with launch or async · 18.4 Handling errors in flows · 18.4.1 Processing upstream exceptions with the catch operator · 18.4.2 Retry the collection of a flow if predicate is true: The retry operator · 18.5 Testing coroutines and flows · 18.5.1 Making tests using coroutines fast: Virtual time and the test dispatcher · 18.5.2 Testing flows with Turbine · 18.6 Summary

What you learn in chapter 18 →

Concepts explained in this book

These guides are generated from the text of Manning books by Manning's knowledge graph and link to the sections they draw on. The full books are available on liveBook and at manning.com.