---
title: 'The Kotlin Language'
source: 'https://academia.sh/en/courses/kotlin'
language: en
updated: '2026-10-21T05:02:39+00:00'
license: 'CC BY-SA 4.0'
lessons: 23
outcomes:
    - "Uses Kotlin's null safety model"
    - 'Writes collection transformations with functional tools'
    - 'Chooses class kinds according to modeling needs'
    - 'Distinguishes whether the equality of two values comes from a singleton object, object identity, content, or only part of the content'
    - 'Reads the basic mechanism of concurrent code written with coroutines and flows'
---

# The Kotlin Language

In Kotlin, what makes two values equal is not the same for every type: an enum entry is always the same object when asked for, two instances of an ordinary class built with the same values are not considered equal, two instances of a data class are equal when their contents match, and in a data class that carries an extra property in its body, two instances that differ in that property still come out equal. The course measures every mechanism with a single question — what makes these two values equal? Variables, basic types, functions and lambdas, null safety, classes, data classes, sealed types, object declarations, delegation, extensions, generics, collections, sequences, coroutines, flows and exceptions all pass through the same frame.

## Language Basics

Kotlin's place on the JVM and its design goals, mutable and immutable declarations, basic types, string templates, control flow with the when expression and loops, functions with default and named parameters, lambdas and higher-order functions, and scope functions.

### Kotlin's Place

Source: https://academia.sh/en/courses/kotlin/kotlins-place

Introduces Kotlin's place on the JVM and its design goals; measures how the same `==` operator gives equality under four different criteria across eight values.

This course teaches Kotlin through a question the language itself answers: when are two values the same? Kotlin is a statically typed, compiled programming language; it compiles to the JVM (Java Virtual Machine), Android, JavaScript, Wasm, and Native targets. This course takes its measurements on the JVM target: the source code is translated to bytecode and lives alongside the JVM's own class library. The design goals fit into two sentences: write readable code with little ceremony, and move type checking to compile time. What the two goals share is that the language sets exact rules about values. The first rule comes right away: in Kotlin, the `==` operator asks for equality, while the `===` operator asks whether two expressions point to the same object.

The next seven lessons in this topic ask the same question about different constructs. Each lesson has one criterion, and one bounding measurement shows the limit of that criterion. The criterion is one of four names. The **singleton** criterion is the case where a value has a single instance in memory and every access reaches the same object. The **identity** criterion is the case where equality depends on the object itself: two objects built the same way are not considered equal. The **content** criterion is the case where equality depends on the values the object carries. The **partial content** criterion is the case where equality looks at only some of the values and ignores the rest.

#### Eight Values, Four Criteria

The measurement is made with the same helper file in every lesson. The `Criterion` class takes a value, its twin built the same way, and one other value, and asks three questions. Is the twin the same object? Is the twin equal under `==`? Is the other value equal under `==`? These three answers decide one of the four criteria.

```kotlin
// Criterion.kt — classifies the criterion that makes two values equal
class Criterion(val label: String, first: Any?, twin: Any?, other: Any?) {
    val sameObject = first === twin
    val equalTwin = first == twin
    val equalOther = first == other

    val name: String = when {
        sameObject -> "singleton"
        !equalTwin -> "identity"
        !equalOther -> "content"
        else -> "partial-content"
    }

    fun report() {
        println("$label: same-object=$sameObject, twin=$equalTwin, other=$equalOther -> $name")
    }
}

fun summarize(checks: List<Criterion>) {
    for (name in listOf("singleton", "identity", "content", "partial-content")) {
        println("$name: ${checks.count { it.name == name }}")
    }
}
```

The program below passes eight values through this class: an enum entry, an object declaration, a plain class, an array, a data class, a list, a string, and a data class that carries an extra property in its body. A data class is a class that derives its equality and its text representation from its constructor properties; its details are not the subject of this lesson and are covered in the Data Classes lesson; here we look only at its equality.

```kotlin
// Main.kt — eight values, four criteria
enum class Color { RED, BLUE }

object Registry
object Archive

class Box(val size: Int)

data class Point(val x: Int, val y: Int)

data class Tag(val name: String) {
    var note: String = ""
}

fun main() {
    val word = listOf("kot", "lin").joinToString("")
    val checks = listOf(
        Criterion("enum-entry", Color.RED, Color.valueOf("RED"), Color.BLUE),
        Criterion("object-declaration", Registry, Registry, Archive),
        Criterion("plain-class", Box(1), Box(1), Box(2)),
        Criterion("array", intArrayOf(1, 2), intArrayOf(1, 2), intArrayOf(1, 3)),
        Criterion("data-class", Point(1, 2), Point(1, 2), Point(1, 3)),
        Criterion("list", listOf(1, 2), listOf(1, 2), listOf(1, 3)),
        Criterion("string", "kotlin", word, "kotlim"),
        Criterion(
            "body-property",
            Tag("a").apply { note = "first" },
            Tag("a").apply { note = "first" },
            Tag("a").apply { note = "last" },
        ),
    )
    for (check in checks) {
        check.report()
    }
    summarize(checks)
}
```

```
enum-entry: same-object=true, twin=true, other=false -> singleton
object-declaration: same-object=true, twin=true, other=false -> singleton
plain-class: same-object=false, twin=false, other=false -> identity
array: same-object=false, twin=false, other=false -> identity
data-class: same-object=false, twin=true, other=false -> content
list: same-object=false, twin=true, other=false -> content
string: same-object=false, twin=true, other=false -> content
body-property: same-object=false, twin=true, other=true -> partial-content
singleton: 2
identity: 2
content: 3
partial-content: 1
```

The output reads as follows. `enum-entry` and `object-declaration` are singletons: an enum entry and an `object` declaration live in memory as a single instance, and the call `Color.valueOf("RED")` returns the same instance as well. `plain-class` and `array` are identity: two `Box(1)` objects are not considered equal even though they carry the same constructor argument, and neither are two `intArrayOf(1, 2)` arrays. On an array, `==` does not look at the elements; it compares the object. `data-class`, `list`, and `string` are content: the twin is equal, the other value is not equal, and the twin is not the same object. The last line, `body-property`, is partial content. Because the `note` property is defined in the body of the data class and not in its constructor, it does not take part in equality; two objects whose `note` values are `first` and `last` come out equal under `==`.

The summary lines confirm this distribution: two singleton, two identity, three content, one partial content. All eight values were classified and none fell into a fifth criterion.

**LB1.** Each of the eight values was measured with a single set of constructor arguments; the classification of instances of the same class built with other arguments is outside this lesson.

**LB2.** Only `intArrayOf` was measured for arrays; that the other array types behave the same way is not a claim measured in this lesson.

#### Kotlin on the JVM

Because Kotlin runs on the JVM, it uses the JVM's own types directly. `java.util.ArrayList` is such a type. The list that Kotlin builds with `listOf` is also a JVM list at run time; both are bound by the same equality rule. The block below measures three things: two JVM lists, a Kotlin list against a JVM list, and the equality of a data class when the value passed to the constructor changes.

```kotlin
// Main.kt — JVM list and the same data class
import java.util.ArrayList

data class Tag(val name: String) {
    var note: String = ""
}

fun main() {
    val jvmList = ArrayList<Int>()
    jvmList.add(1)
    jvmList.add(2)
    val jvmTwin = ArrayList<Int>()
    jvmTwin.add(1)
    jvmTwin.add(2)
    println(jvmList.javaClass.name)
    val checks = listOf(
        Criterion("jvm-list", jvmList, jvmTwin, listOf(1, 3)),
        Criterion("kotlin-and-jvm-list", listOf(1, 2), jvmList, listOf(1, 3)),
        Criterion("data-class-constructor", Tag("a"), Tag("a"), Tag("b")),
    )
    for (check in checks) {
        check.report()
    }
    summarize(checks)
}
```

```
java.util.ArrayList
jvm-list: same-object=false, twin=true, other=false -> content
kotlin-and-jvm-list: same-object=false, twin=true, other=false -> content
data-class-constructor: same-object=false, twin=true, other=false -> content
singleton: 0
identity: 0
content: 3
partial-content: 0
```

The first line prints the JVM's class name: `java.util.ArrayList`. `jvm-list` is content, because two lists that carry the same elements are equal and are separate objects. `kotlin-and-jvm-list` is content as well: a list that Kotlin builds with the expression `listOf(1, 2)` comes out equal under `==` to a JVM list with the same elements. A Kotlin type and a JVM type do not form separate worlds as far as equality goes. `data-class-constructor` is the constructor-bound form of the `Tag` class we saw earlier in this lesson; when the `name` in the constructor differs, equality breaks, which means equality looks at the constructor properties.

**LB3.** In this section `listOf` and `java.util.ArrayList` were compared only for equality; the run-time class of the `listOf` expression is printed separately in a later section.

**LB4.** The JVM list was measured with `java.util.ArrayList`; the JVM's other collection classes are outside this lesson.

#### What a Data Class Derives

A data class derives the members `equals`, `hashCode`, `toString`, `componentN`, and `copy` from the properties in its primary constructor; this section measures the first three. The previous measurement showed only the `==` side. The block below asks all three at once of a `Tag` class that carries a `note` property in its body. The `note` value of the second object is changed afterward; the constructor argument is the same in both objects.

```kotlin
// Main.kt — data class: equals, hashCode, and toString
data class Tag(val name: String) {
    var note: String = ""
}

fun main() {
    val first = Tag("a")
    val second = Tag("a")
    second.note = "second"
    println(first == second)
    println(first.hashCode() == second.hashCode())
    println(first)
    println(second)
    println(first.note.isEmpty())
}
```

```
true
true
Tag(name=a)
Tag(name=a)
true
```

The first two lines give `true`: `==` and `hashCode` do not see the difference. The next two lines show that the representation printed by `println` does not carry the `note` value. Although `second.note` is now `"second"`, the screen shows `Tag(name=a)`. The last line confirms that `first.note` is still empty; so the two objects really are in different states, and the three derived behaviors ignore this difference together.

The common mistake appears exactly here. In a debugging session, someone prints two objects with `println`, sees the same text, and concludes "these are the same"; yet the property in the body stands apart. For this reason every value that should take part in equality must be a constructor property. In Java, `equals` and `hashCode` are written by hand, and keeping the two consistent with each other is the developer's job; in Kotlin, both are derived together from the same constructor properties, so they do not fall out of step unless one of them is overridden by hand.

**LB5.** The Java comparison was not measured in this section; writing `equals` and `hashCode` on the Java side was mentioned as known behavior, and it is not a claim executed in this lesson.

#### Run-Time Classes

In Kotlin source there is a single name `Int`; on the JVM, the `int` primitive and the `java.lang.Integer` box are two separate things. We can read which class the same value lives in at run time with `javaClass.name`. The block prints the class names of five values and three lists.

```kotlin
// Main.kt — JVM classes of Kotlin values
fun main() {
    val number: Any = 1
    val text: Any = "kotlin"
    println(number.javaClass.name)
    println(text.javaClass.name)
    println(listOf(1, 2).javaClass.name)
    println(listOf(1).javaClass.name)
    println(emptyList<Int>().javaClass.name)
    println(mutableListOf(1).javaClass.name)
    println(intArrayOf(1).javaClass.name)
}
```

```
java.lang.Integer
java.lang.String
java.util.Arrays$ArrayList
java.util.Collections$SingletonList
kotlin.collections.EmptyList
java.util.ArrayList
[I
```

The `1` held with the type `Any` comes out as `java.lang.Integer`: when a value enters an `Any` variable, it is boxed. The string appears as `java.lang.String`; Kotlin has no separate string class. For `intArrayOf(1)` the class name is `[I`, which is the JVM's notation for `int[]`.

The lists are more instructive. The expression `listOf(1, 2)` gives `java.util.Arrays$ArrayList`, while `listOf(1)` gives `java.util.Collections$SingletonList`. The empty list gives `kotlin.collections.EmptyList`. `mutableListOf(1)` returns `java.util.ArrayList`. The reason for the mechanism is this: `listOf` does not build a single class, it chooses the cheapest implementation according to the number of elements. Code that relies on the run-time class of a `List` value is therefore fragile: four separate calls gave four separate classes. Equality, on the other hand, rests on the elements and not on the class name; in the previous section, `listOf` and `java.util.ArrayList` came out equal under the content criterion.

Java comparison: in Java, `int` and `Integer` are written separately in source code, and the developer chooses the boxing. In Kotlin, the compiler makes the choice. This observation was measured with a variable of type `Any`; that boxing would also happen with the type `Int?` was not measured in this lesson.

**LB6.** The class names were measured with Kotlin 2.4.21 and the JVM in the sandbox image; on another JVM version, `listOf` may not choose the same internal classes.

#### Comparing with Null

The `==` operator also works on nullable types. The block below makes five comparisons between an empty `Tag?` and a filled `Tag?`.

```kotlin
// Main.kt — comparing with null
data class Tag(val name: String)

fun main() {
    val missing: Tag? = null
    val present: Tag? = Tag("a")
    println(missing == null)
    println(missing == present)
    println(present == Tag("a"))
    println(missing === missing)
    val none: String? = null
    println(none == none)
}
```

```
true
false
true
true
true
```

The five lines say the following in order. A null value is equal to `null`. A null value is not equal to a filled object, and this comparison does not throw an error. A filled object is equal to another `Tag` built with the same constructor argument. `===` works on null too. Two null strings are equal to each other.

The mechanism is this: the expression `a == b` asks whether `b` is also null when `a` is null; when `a` is filled, `a.equals(b)` is called. The compiler adds this null check itself. The common mistake is to write this check by hand: an expression of the form `a != null && a.equals(b)` carries extra words. In Java, the call `a.equals(b)` throws a `NullPointerException` when `a` is null; for a null-safe comparison in Java, `Objects.equals` is used. In Kotlin, the same guarantee lies in the `==` operator itself.

**LB7.** These five comparisons were measured on `Tag` and `String`; the `NullPointerException` behavior on the Java side and `Objects.equals` were written as a reminder and were not executed in this lesson.

#### Habits That Come from the Measurements

The measurements in this lesson can be tied to three habits. The first is to measure the equality behavior of a class instead of guessing it. Because `Tag` is a data class, its two separate instances came out equal under `==` but not under `===`; in a plain class both would give `false`. Anyone who will use a class as a key in a collection or compare it with `assertEquals` in a test must first know the difference between these two operators. In Java, `==` looks at identity and `equals` looks at content; in Kotlin, `==` looks at content and `===` looks at identity. A person coming from Java may think that writing `==` compares identity, and this is a point that misleads code reading on the first pass between the two languages.

The second is not to rely on the run-time class. The `listOf` calls in this lesson produced different JVM classes: the single-element list, the empty list, and the multi-element list came with separate implementations. These class names are not a language contract; they may change when the Kotlin version or the target changes. A check such as `list is ArrayList` is fragile for this reason. The contract is the `List` interface itself.

The third is to choose the readable form when comparing with `null`. A `null` check with `==` handles the `null` case separately before the object's `equals` method is called; so `null == null` is true, and writing `x == null` does not throw a `NullPointerException`. In Java, writing `x.equals(y)` raises an exception if `x` is null. Kotlin closes this off through the definition of the operator, and the measurement also showed that none of the five comparisons raised an exception.

What the three habits share is that nothing is claimed without being measured. The rest of the lesson proceeds by this principle: every claim either rests on an output or is marked "not measured" in an `LB` line. The practical consequence for the reader is this: if you are unsure about a behavior, writing and running a small single-file program takes less time than searching the documentation. This is also why each lesson in this course has a criterion: the criterion fixes in advance which question you ask with which operator and makes the output easier to interpret. For a data class the content criterion applies, for a plain class identity, and for a singleton object singleton. To know the criterion of a type is to know what every comparison written with that type means. Code that assumes the wrong criterion usually compiles and runs; the error shows only when two objects come out unexpectedly equal or different.

#### The Bounding Measurement

The limit of the criterion runs through the inside of the data class. The same `Tag` class carries two criteria together: the `name` in the constructor takes part in equality, and the `note` in the body does not. `Tag("a")` and `Tag("a")` are content, and `Tag("a")` and `Tag("b")` are not equal; but when `name` stays the same and only `note` changes, `==` still gives `true`, so we land in partial content. The criterion of a class therefore comes not from the name of the class but from which properties it builds its `equals` behavior on.

We saw this claim in two measurements: `data-class-constructor` is content, and `body-property` is partial content.

#### Summary

- Kotlin is a statically typed, compiled language; it compiles to more than one target, and on the JVM target measured in this course it works directly with JVM types.
- `==` asks for equality, while `===` asks for object identity.
- Equality rests on one of four criteria: singleton, identity, content, or partial content.
- The equality of a data class looks at its constructor properties; a property in the body does not take part in equality.
- This lesson's criterion: **content**; bounding measurement: when the `name` in the constructor differs, the same data class is not equal.

#### Next Step

We have seen the four criteria of equality. In the next lesson we start from the smallest construct: a variable's name, its type, and whether it can be reassigned. We will measure whether the difference between `val` and `var` also changes how the same value behaves in two variables.

### Variables

Source: https://academia.sh/en/courses/kotlin/variables

Introduces declaration with `val` and `var`, type inference, and the `const val` constant; measures that the same string constant is a single object in two variables.

In the previous lesson we saw the four criteria of equality across eight values. In this lesson we look at how a value is bound to a name: variables declared with `val` and `var`. The question is this: if I bind the same constant to two separate variables, are two separate objects created, or is a single object given two names?

#### val, var, and Type Inference

In Kotlin, you declare a variable with either `val` or `var`. A `val` is a read-only variable: once the first value is given, no other value can be assigned to the same name. A `var` can be reassigned. Neither form forces you to write the type when you give a value; the compiler finds the type from the expression on the right. This inference (type inference) happens at compile time, and the type of the variable stays fixed afterward. If you want to write the type explicitly, you put a colon and the type between the name and the value.

It would be wrong to think that the object a `val` variable is bound to never changes: `val` says only that the name can never be bound to another value again. If the object it is bound to can change internally, it can change. This distinction will be useful in later lessons; here we look only at whether the name can be reassigned.

In this lesson too, the measurements are made with the helper file introduced in the first lesson. The `Criterion` class takes a value, its twin, and a different value, and asks three questions; the file is given below exactly as it was.

```kotlin
// Criterion.kt — classifies the criterion that makes two values equal
class Criterion(val label: String, first: Any?, twin: Any?, other: Any?) {
    val sameObject = first === twin
    val equalTwin = first == twin
    val equalOther = first == other

    val name: String = when {
        sameObject -> "singleton"
        !equalTwin -> "identity"
        !equalOther -> "content"
        else -> "partial-content"
    }

    fun report() {
        println("$label: same-object=$sameObject, twin=$equalTwin, other=$equalOther -> $name")
    }
}

fun summarize(checks: List<Criterion>) {
    for (name in listOf("singleton", "identity", "content", "partial-content")) {
        println("$name: ${checks.count { it.name == name }}")
    }
}
```

The program below shows the four faces of declaration together. `city` cannot be reassigned, `visits` is reassigned, `ratio` writes its type explicitly, and `inferred` takes its type from inference. The `const val` at the top of the file is a compile-time constant: a constant whose value is known during compilation, which can be only a primitive type or a string, and which stands at the top level or inside an object declaration.

```kotlin
// Main.kt — val, var, type inference, and const val
const val MAX_VISITS = 3

fun main() {
    val city = "Ankara"
    var visits = 1
    visits = visits + 1
    val ratio: Double = 2.0
    val inferred = 7
    println("$city, visits: $visits, ratio: $ratio")
    println("inferred type: ${inferred::class.simpleName}")
    println("upper limit: $MAX_VISITS")
}
```

```
Ankara, visits: 2, ratio: 2.0
inferred type: Int
upper limit: 3
```

In the output, the line `Ankara, visits: 2, ratio: 2.0` shows that the `visits` variable was reassigned once. The line `inferred type: Int` confirms that the type inferred from the constant `7` is `Int`; we asked for it at run time with `inferred::class.simpleName`. The last line prints the value of the `const val` constant. The same output was obtained in the sandbox with Kotlin 2.4.21.

**LB8.** The inferred type was measured only with the constant `7`; what inference does in contexts that require `Long` or `Double` is the subject of the next lesson.

**LB9.** `const val` was measured only at the top of the file; the rules for its form inside an `object` or a `companion object` are outside this lesson.

The practical consequence of this output is as follows. A counter such as `visits` wants `var`, because its value changes in the flow of the program. A name such as `city` wants `val`, because binding it to another value after the first assignment would be a mistake. The decision is made according to the role of the variable; whether the type is written or inferred does not affect it.

#### Reassignment Is Rejected

Assigning a value a second time to a variable declared with `val` is rejected by the compiler. We saw this by compiling in the sandbox; the fragment below is therefore without output and carries no `main`.

```kotlin
val limit = 10
limit = 20
```

The compiler rejects the second line because a `val` cannot be reassigned, and the program never runs. This makes reassignment a compile-time problem and not a run-time one. In Kotlin, `val` is the default choice; `var` is written only when the value really has to be reassigned. A reader looking at a name then does not have to search for whether that name is bound to another value somewhere.

**LB10.** The rejection was measured with a single local `val`; the form of the error message for a top-level `val` and for a `val` that is a class property was not verified in this lesson.

#### The Same Constant, Two Variables

Now the measurement of the lesson. There are four observations. `val-literal`: two separate `val` variables are bound to the same `"kotlin"` constant. `var-literal`: a `var` is compared with the same constant. `val-built` and `var-built` build the same text at run time, one with `joinToString` and the other with `buildString`. In this way we measure whether it makes a difference that the declaration form is `val` or `var`.

In every observation, `Criterion` takes three things: the measured value, a twin built the same way, and a different value. In the constant observations, the twin is a second variable or the constant itself. In the built observations, the twin is produced by calling the same method once more; so both sides are created separately at run time. The different value is each time the string `kotlim`, with one letter changed.

```kotlin
// Main.kt — the same literal in two variables
fun main() {
    val first = "kotlin"
    val twin = "kotlin"
    var changeable = "kotlin"
    val built = listOf("kot", "lin").joinToString("")
    var builtVar = buildString {
        append("kot")
        append("lin")
    }
    val checks = listOf(
        Criterion("val-literal", first, twin, "kotlim"),
        Criterion("var-literal", changeable, "kotlin", "kotlim"),
        Criterion("val-built", built, listOf("kot", "lin").joinToString(""), "kotlim"),
        Criterion("var-built", builtVar, buildString {
            append("kot")
            append("lin")
        }, "kotlim"),
    )
    for (check in checks) {
        check.report()
    }
    summarize(checks)
}
```

```
val-literal: same-object=true, twin=true, other=false -> singleton
var-literal: same-object=true, twin=true, other=false -> singleton
val-built: same-object=false, twin=true, other=false -> content
var-built: same-object=false, twin=true, other=false -> content
singleton: 2
identity: 0
content: 2
partial-content: 0
```

`val-literal` is singleton: `first` and `twin` are the same object, `same-object=true`. The same string constant is held as a single object during compilation, and every expression that uses it reaches the same object. `var-literal` is singleton too; using `var` does not break the constant being the same object, because `var` affects only whether the name can be reassigned. `val-built` and `var-built` are content: `joinToString` and `buildString` produce a new string at run time, so `same-object=false`; with the twin `==` still gives `true`, and with the different value `false`.

The summary lines show two singleton and two content. So binding a value with `val` or with `var` does not decide the criterion; what decides it is where the value comes from: a constant, or a value built at run time.

**LB11.** That a string constant is a single object was measured on the JVM target of Kotlin 2.4.21; this behavior rests on the JVM's string pool, and it is not claimed to be the same on other targets.

**LB12.** The built strings were measured with `joinToString` and `buildString`; that other ways of building give the same result was not measured in this lesson.

#### val Does Not Freeze the Bound Object

Above, we said for `val` that "the name can never be bound to another value again." The limit of this sentence is that the bound object itself may change. The block below builds a mutable list declared with `val` and adds an element to it. It then shows a `val` whose first value is not given on the declaration line but assigned afterward.

```kotlin
// Main.kt — val can change the bound object, the first value can be given later
fun main() {
    val names = mutableListOf("ann")
    names.add("bob")
    println(names)
    val assigned: Int
    val flag = names.size > 1
    if (flag) {
        assigned = 10
    } else {
        assigned = 20
    }
    println(assigned)
}
```

```
[ann, bob]
10
```

`names` is a `val`; even so, the `add` call turns the list into `[ann, bob]`. Nothing raises an error, because the name `names` is still bound to the same list; what changed is the inside of the list. The second variable, `assigned`, receives no value on its declaration line, its type is written, and it is assigned exactly once in each of the `if` and `else` branches. The compiler accepts this pattern and prints `10` on the screen. The rule is this: a `val` variable can receive a value at most once on every path of its scope.

A common mistake is to read the word `val` as "the object cannot be changed." If you want to freeze the contents of a list, a read-only collection type is chosen (`listOf`); `val` alone does not provide this. Java comparison: a `final` variable in Java carries the same limit, an element can be added to a `final` list, and a `final` variable whose first value is given afterward is also a known pattern in Java. These two Java behaviors were not measured here; they were mentioned as known behavior.

**LB13.** In this section the measurement used `MutableList` and `Int`; whether the properties change when a `val` is bound to a `data class` instance was not measured in this lesson.

#### Changing a var from Inside a Lambda

Kotlin allows a lambda to read and change a `var` variable outside itself. The block below updates a total and a call counter from inside `forEach`; then it copies the current value of the total into a `val` and changes the original `var` variable.

```kotlin
// Main.kt — changing a var from inside a lambda
fun main() {
    var total = 0
    var calls = 0
    listOf(1, 2, 3).forEach {
        total += it
        calls++
    }
    println("total: $total, calls: $calls")
    val snapshot = total
    total = 100
    println("copy: $snapshot, var: $total")
}
```

```
total: 6, calls: 3
copy: 6, var: 100
```

The first line gives `total: 6, calls: 3`: the lambda ran three times and both variables outside were updated. The second line shows that the copy stayed separate. The value of `snapshot` stays `6`, while `total` becomes `100`. Because an `Int` value is copied at the moment it is assigned, a `var` that changes afterward is not reflected in the copy.

The reason for the mechanism is that the lambda captures the outside variable not as a value but by carrying the variable itself; the lambda and the outside code work on the same `var`. In Java, for the same job, the local variable that the lambda captures must be effectively `final`, so a container such as an array or an `AtomicInteger` is used to keep a counter. In Kotlin there is no need for such a workaround. This Java rule and how Kotlin carries the captured variable behind the scenes were not measured in this lesson; what was measured is only that the outside `var` values are current after the lambda.

**LB14.** The captured variable was of type `Int` and was measured with `forEach`; the behavior of the same update in a multi-threaded context is outside this lesson.

#### const val and Concatenation

At the start of the lesson, we said of `const val` "a constant whose value is known during compilation." This definition has a consequence: the concatenation of two `const val` strings is also computed during compilation. The block below compares the concatenation of two `const val` with a top-level `val` that builds the same text at run time with `joinToString`. In both measurements the twin comes from writing the same expression once more.

```kotlin
// Main.kt — const val concatenation and a run-time value
const val PREFIX = "kot"
const val FULL = PREFIX + "lin"

val runtimeFull = listOf("kot", "lin").joinToString("")

fun main() {
    val checks = listOf(
        Criterion("const-concat", FULL, "kotlin", "kotlim"),
        Criterion("runtime-top-level", runtimeFull, "kotlin", "kotlim"),
    )
    for (check in checks) {
        check.report()
    }
    summarize(checks)
}
```

```
const-concat: same-object=true, twin=true, other=false -> singleton
runtime-top-level: same-object=false, twin=true, other=false -> content
singleton: 1
identity: 0
content: 1
partial-content: 0
```

`const-concat` is singleton: the expression `PREFIX + "lin"` turns into the constant `"kotlin"` at compile time and is bound to the single object in the string pool. `runtime-top-level` is content: when the same text is built at run time, it becomes a separate string, `same-object=false`. The summary line shows one singleton and one content.

The common mistake here is to treat `const val` and a top-level `val` variable as interchangeable. Both stand at the top of the file and neither can be reassigned; but one takes its value during compilation and the other while the program runs. The boundary case follows from this: a `const val` is built only with values the compiler can compute. Java comparison: a `static final` field in Java carries a similar distinction, since there are `static final` fields that are compile-time constants and ones that are not. This Java distinction was not measured.

**LB15.** This comparison was measured with two `const val` strings and `joinToString`; the behavior of numeric `const val` concatenations was not measured in this section.

#### Habits That Come from the Measurements

In this lesson's measurements, the difference between `val` and `var` appeared as the difference between binding a variable and the contents of an object. A `val` means a name bound once; if the bound object is a mutable list, an element can be added to the list, and the output `[ann, bob]` showed this. So saying "immutable" for a `val` calls for care: what cannot be reassigned is the binding, not the object. To protect the contents, a read-only collection type or `val` fields inside a `data class` are used.

When a `var` was changed from inside a lambda, the counter was visible outside too: `total: 6, calls: 3`. The measurement showed that the lambda and the outside code look at the same variable; it is known that underneath this lies the variable being wrapped in a box, but this was not measured in this lesson. The copied value, on the other hand, stayed separate: in the line `copy: 6, var: 100`, the copy was not affected when the `var` was reassigned. A common mistake is to think that when a value is captured and the variable is changed later, the captured value changes too; the difference between a copy and a reference has to be told apart by looking at where the assignment is made.

A `const val` is a compile-time constant and can take only a primitive type or a string value; it is defined at the top level, in an object declaration, or in a companion object. In the measurement, the concatenation of two `const val` appeared as a single object, while a concatenation computed at run time gave a value equal in content but separate. The practical rule is this: if a value can be known at compile time, write `const val`; if it cannot, write `val`. In Java, the same distinction is expressed with `static final`, but whether a field is a compile-time constant depends on its initializer expression and cannot be read from the declaration.

If these three observations had to be reduced to one sentence: ask whether a name changes separately from whether the object the name is bound to changes. Each time you see a `val` while reading code, keeping these two questions apart catches a large part of mistakes in advance.

#### The Bounding Measurement

The singleton criterion is limited to the constant. When the same text is built at run time, the criterion turns back to content: `val-built` and `var-built` are two examples of this. The same `"kotlin"` text falls under the singleton criterion when it is written as a constant in one place, and under the content criterion when it is built with `joinToString` or `buildString` in another. For this reason, when comparing text in code, `==` is used instead of `===`; the former gives the right answer in both cases.

#### Summary

- `val` is a variable that cannot be reassigned, and `var` is one that can; the type of both can be found by inference.
- `const val` is a compile-time constant; its value must be known during compilation.
- A second assignment to a `val` variable is rejected by the compiler.
- Choosing `val` or `var` does not decide the equality criterion; whether the value comes from a constant or from run time does.
- This lesson's criterion: **singleton**; bounding measurement: the same text built at run time falls under the content criterion.

#### Next Step

The type of a variable was found by inference. In the next lesson we look at basic types: numbers, the character, and the logical value. The same number `1` is an `Int` in one context and a `Long` in another; we will measure why an `==` that mixes these two types is rejected.

### Basic Types

Source: https://academia.sh/en/courses/kotlin/basic-types

Introduces the number types, the character, and the logical value; measures that the criterion of equality changes once a nullable `Int` is boxed.

In the previous lesson we saw that the type inferred from the constant `7` is `Int`. Now we look at the basic types: `Int` and `Long` for integers, `Double` for floating-point numbers, `Char` for a single character, and `Boolean` for the logical value. The question of equality has two faces for these types. First, can numbers of different types be compared with `==`? Second, when a number is boxed, can the same value split into two objects?

#### Types and Explicit Conversion

In Kotlin, an integer constant is an `Int` by default. A constant that exceeds the `Int` limit becomes a `Long` on its own; the `L` suffix is written to make a small value a `Long` as well. A constant with a decimal point is a `Double`. A `Char` is written in single quotes and gives its numeric code with `.code`. Conversions are explicit: an `Int` is not assigned to a `Long` variable automatically; you write `count.toLong()`. Arithmetic, on the other hand, can mix types: when an `Int` and a `Double` are added, the result is a `Double`.

In this lesson too, the measurements are made with the helper file introduced in the first lesson. The `Criterion` class takes a value, its twin, and a different value, and asks three questions; the file is given below exactly as it was.

```kotlin
// Criterion.kt — classifies the criterion that makes two values equal
class Criterion(val label: String, first: Any?, twin: Any?, other: Any?) {
    val sameObject = first === twin
    val equalTwin = first == twin
    val equalOther = first == other

    val name: String = when {
        sameObject -> "singleton"
        !equalTwin -> "identity"
        !equalOther -> "content"
        else -> "partial-content"
    }

    fun report() {
        println("$label: same-object=$sameObject, twin=$equalTwin, other=$equalOther -> $name")
    }
}

fun summarize(checks: List<Criterion>) {
    for (name in listOf("singleton", "identity", "content", "partial-content")) {
        println("$name: ${checks.count { it.name == name }}")
    }
}
```

The program below runs all of these rules together. The `total` variable converts an `Int` value to a `Long` and multiplies it by a billion. `7 / 2` and `7 / 2.0` compare two divisions: in the first both sides are integers, in the second one side has a decimal point. `letter + 1` adds a number to a `Char`.

```kotlin
// Main.kt — numbers, character, logical value, and explicit conversion
fun main() {
    val count: Int = 3
    val total: Long = count.toLong() * 1_000_000_000L
    val size = 2.5
    val letter = 'K'
    val ready = count > 2
    println(count + size)
    println(total)
    println(7 / 2)
    println(7 / 2.0)
    println(letter.code)
    println(letter + 1)
    println(ready)
}
```

```
5.5
3000000000
3
3.5
75
L
true
```

`count + size` gives 5.5: an `Int` and a `Double` were added and the result became a `Double`. `total` prints 3000000000; because this value exceeds the `Int` limit, a `Long` was needed. `7 / 2` gives three, because both sides are integers, so the division is carried out as an integer division and the fractional part is discarded. `7 / 2.0`, on the other hand, gives 3.5. `letter.code` prints 75, the code of the capital letter `K`. `letter + 1` gives the letter `L`, the next character. The last line shows that the `ready` variable is `true`.

**LB16.** In this section the division was measured with the constants `7` and `2`; the behavior of division with a negative number is measured in a separate section.

**LB17.** `Char` arithmetic was measured only with `letter + 1`; the type of the difference between two `Char` values was not measured in this lesson.

#### Conversion Is Explicit, and So Is Comparison

That arithmetic can mix types does not hold for assignment and comparison. Assigning an `Int` value to a variable of type `Long` is rejected by the compiler; we saw this by compiling in the sandbox, so the fragment is without output.

```kotlin
val count: Int = 3
val wide: Long = count
```

The compiler reports that it found an `Int` where a `Long` was expected. Comparison follows the same rule: comparing an `Int` with a `Long` constant by `==` again does not compile.

```kotlin
val count: Int = 1
println(count == 1L)
```

The compiler reports that the `==` operator cannot be applied between `Int` and `Long`. The solution is to write the conversion explicitly: `count.toLong() == 1L`. But if the types are held as `Any`, the situation changes: the compiler cannot know the static type, so it asks for equality of the run-time objects.

```kotlin
// Main.kt — numbers held as Any
fun main() {
    val a: Any = 1
    val b: Any = 1L
    println(a == b)
}
```

```
false
```

`1` and `1L` did not come out equal when held as `Any`: the output is `false`. An `Int` object and a `Long` object belong to different classes even if their numeric values are the same, and `equals` takes this difference into account. So the comparison that the compiler rejected is not rejected once its type is hidden, but its answer is still `false`.

**LB18.** The compiler errors were obtained with Kotlin 2.4.21; the message wording may change in later versions, but what is measured in this lesson is the rule that the two comparisons are rejected.

#### Boxed Numbers and NaN

The measurement of the lesson looks at two things. First, `Int?`, a nullable type, holds a boxed number; on the JVM this is an `Integer` object. Are two boxed numbers the same object even when they have the same value? Second, `Double.NaN` (the not-a-number value) gives different equality depending on the static type. First the comparison in which both sides are `Double` is printed, then the comparison in which the same value is held as `Any`.

```kotlin
// Main.kt — boxed numbers and NaN
fun main() {
    val boxedBig: Int? = 1000
    val boxedBigTwin: Int? = 1000
    val boxedSmall: Int? = 127
    val boxedSmallTwin: Int? = 127
    val nan = Double.NaN
    val anyNan: Any = nan
    val anyNanTwin: Any = Double.NaN
    println(nan == nan)
    println(anyNan == anyNan)
    val checks = listOf(
        Criterion("boxed-1000", boxedBig, boxedBigTwin, 1001),
        Criterion("boxed-127", boxedSmall, boxedSmallTwin, 128),
        Criterion("nan-as-any", anyNan, anyNanTwin, 1.0),
    )
    for (check in checks) {
        check.report()
    }
    summarize(checks)
}
```

```
false
true
boxed-1000: same-object=false, twin=true, other=false -> content
boxed-127: same-object=true, twin=true, other=false -> singleton
nan-as-any: same-object=false, twin=true, other=false -> content
singleton: 1
identity: 0
content: 2
partial-content: 0
```

The first two lines are NaN equality. `nan == nan`, because the static type is `Double`, follows the IEEE rule and gives `false`; NaN is not equal to itself. `anyNan == anyNan` gives `true`, because when the static type is `Any`, `==` calls the object's `equals` method, and `equals` treats NaN as equal to itself. The same number gives two different answers depending on the type it is held in.

Among the three measurement lines, `boxed-1000` is content: the two `Int?` variables have the value 1000 but are separate objects, `same-object=false`. `boxed-127` is singleton: the same value points to the same object on the JVM, `same-object=true`. Two numbers with the same type and the same form of value fell under different criteria according to the value. `nan-as-any` is content: with the twin `==` gives `true`, and they are separate objects.

**LB19.** 127 and 1000 were measured on the JVM, with Kotlin 2.4.21; no general limit on which values are shared is drawn in this lesson, and no claim is made for values other than 127 and 1000.

**LB20.** The NaN observation was measured with a single `Double.NaN` value held as `Any`; that `Float.NaN` gives the same result was not measured in this lesson.

#### Overflow, Division by Zero, and Decimal Error

That a type has a limit calls for knowing what happens when the limit is exceeded. The block below shows three limit cases together: a sum that exceeds the `Int` limit, integer division by zero, and decimal division by zero. It then tests the error that decimal numbers inherit from binary representation with `0.1 + 0.2`. The `Math.addExact` call, which catches overflow as an error, is a method of the JVM itself; Kotlin can call it directly.

```kotlin
// Main.kt — overflow, division by zero, and decimal error
fun main() {
    val zero = 0
    val big = 3_000_000_000
    println(Int.MAX_VALUE + 1)
    println(Long.MAX_VALUE)
    println(big::class.simpleName)
    try {
        Math.addExact(Int.MAX_VALUE, 1)
    } catch (e: ArithmeticException) {
        println(e.message)
    }
    try {
        println(1 / zero)
    } catch (e: ArithmeticException) {
        println(e.message)
    }
    println(1.0 / 0.0)
    println(0.1 + 0.2)
    println(0.1 + 0.2 == 0.3)
}
```

```
-2147483648
9223372036854775807
Long
integer overflow
/ by zero
Infinity
0.30000000000000004
false
```

`Int.MAX_VALUE + 1` does not raise an error; it silently becomes `-2147483648`: the number wraps from the largest value to the smallest. For `Long.MAX_VALUE`, `9223372036854775807` is printed. Because the constant `3_000_000_000` exceeds the `Int` limit, type inference makes it a `Long` by itself; the `Long` in the output confirms this. `Math.addExact`, on the other hand, throws an error with the message `integer overflow` for the same sum; so if you want overflow to be noticed, you choose this path.

When an integer is divided by zero, the message `/ by zero` comes. In decimal division the same operation raises no error and returns `Infinity`. The last two lines show the limit of the decimal number: the result of `0.1 + 0.2` is `0.30000000000000004`, and the `==` comparison with `0.3` gives `false`.

The common mistake appears in two places. The first is forgetting that overflow raises no error and accumulating a sum in an `Int`. The second is comparing two decimal numbers with `==`; for values that must come out exact, such as money, a decimal type is the wrong choice. Java comparison: these outputs are the same as in Java, because Kotlin uses the JVM's own arithmetic here; Kotlin does not require a separate type to be written for this behavior. This sameness is stated on the basis of the run-time environment being the same, not by running Java code.

**LB21.** Overflow was measured with an `Int` sum, and division by zero with `Int` and `Double`; that `Long` overflow and `Float` give the result in the same form was not measured in this lesson.

#### Narrowing Conversions

A widening conversion causes no trouble; the trouble appears where a value is fitted into a narrower type. The block below measures five conversions and the conversion from text to number: converting `300` to `Byte`, decimal numbers to `Int`, `65` to `Char`, and a valid or an invalid string to `Int`.

```kotlin
// Main.kt — narrowing conversions and number from text
fun main() {
    println(300.toByte())
    println(3.99.toInt())
    println((-3.99).toInt())
    println(65.toChar())
    println("42".toInt() + 1)
    println("x".toIntOrNull())
    try {
        "x".toInt()
    } catch (e: NumberFormatException) {
        println(e.message)
    }
}
```

```
44
3
-3
A
43
null
For input string: "x"
```

`300.toByte()` cannot carry more than eight bits and becomes `44`: only the low eight bits remain, the upper bits are discarded, and no error is raised. `3.99.toInt()` does not round; it truncates the fractional part, and `3` comes out. For a negative value, `(-3.99).toInt()` also gives `-3`, so there is truncation toward zero, not rounding down. `65.toChar()` gives the capital letter `A`; the bridge between a number and a character code is explicit.

There are two ways to convert text to a number. `"42".toInt() + 1` gives `43` on valid text. On invalid text the two options part ways: `toIntOrNull()` returns `null` instead of throwing an error, while `toInt()` throws a `NumberFormatException` with the message `For input string: "x"`. For text that comes from a user, the first way asks for a `null` check but does not bring the program down, while the second way does.

The reason for the mechanism is that conversions are written explicitly. The compiler does not assign silently to a narrow type; writing `toByte()` says that you knowingly accept the loss of information. In Java, the same narrowing is written with a cast of the form `(byte) 300`, and it is often overlooked.

**LB22.** The conversions were measured with `Byte`, `Int`, and `Char`; that the same truncation rules hold for `Short`, `Float`, and `Long` was not measured in this lesson.

#### Division and Modulo with Negative Numbers

In the first section we saw that `7 / 2` discards the fractional part. But what if the dividend is negative? The block below shows four ways of writing a negative division and remainder operation, then three other limit cases together: the absolute value of the smallest `Int`, hexadecimal and binary constant notation, and ordering between two `Char` values.

```kotlin
// Main.kt — division and modulo with negative numbers
import kotlin.math.abs

fun main() {
    println(-7 / 2)
    println(-7 % 2)
    println((-7).mod(2))
    println(Math.floorMod(-7, 2))
    println(abs(Int.MIN_VALUE))
    println(0xFF)
    println(0b1010)
    println('a' < 'b')
}
```

```
-3
-1
1
1
-2147483648
255
10
true
```

`-7 / 2` gives `-3`; the fractional part is discarded toward zero, not downward. `-7 % 2`, on the other hand, gives `-1`: the sign of the remainder follows the sign of the dividend. If you want the remainder always to come out zero or positive, you need `(-7).mod(2)` or the JVM's `Math.floorMod(-7, 2)` call; both gave `1`. A common mistake is forgetting that a negative remainder can come out when converting the index of an array with `%`.

`abs(Int.MIN_VALUE)` gives `-2147483648`; even the absolute value does not come out positive, because `+2147483648` has no counterpart in `Int`. The constant `0xFF` prints `255`, and the constant `0b1010` prints `10`; writing a constant in hexadecimal or binary does not change the type itself, only how it is written. The expression `'a' < 'b'` gives `true`, because `Char` values are ordered by character code.

Java comparison: the `%` operator in Java also follows the sign of the dividend. The `mod` that Kotlin adds offers, as a method next to the operator, the result obtained with `Math.floorMod` in Java. Both of these results were observed in the output above.

**LB23.** Negative division and remainder were measured with a single pair (`-7`, `2`); that the same sign rule holds for a negative divisor or for the `Long` type was not measured in this lesson.

#### Habits That Come from the Measurements

In numeric types, Kotlin's guarantees are limited, and this lesson's measurements showed where the limit begins. Overflow is silent: the output of `Int.MAX_VALUE + 1` was `-2147483648` and no exception was thrown. This behavior is the same as in Java. Where overflow can be harmful, for example in a money calculation or in the sum of durations, you need to use `Long` or to write checked operations such as `Math.addExact`. For a `Long`, the same overflow lies at a much more distant limit and is enough in most cases, but it does not remove the limit.

Division by zero throws an exception for integers (`/ by zero`), while for decimal numbers it gives `Infinity`. This difference means that when the type of a calculation is changed, the error behavior changes too. A piece of code that uses `Double` quietly keeps running when divided by zero and spreads the result; to notice the error, an `isInfinite` or `isNaN` check is needed. Because decimal numbers are kept in base two, the output of `0.1 + 0.2` was `0.30000000000000004` and the `== 0.3` comparison gave `false`. Where an exact value is wanted, such as money, `BigDecimal` or an integer converted to the smallest currency unit is used.

Narrowing conversions also call for attention. Converting from a larger type to a smaller one truncates or wraps the number, and results such as `44`, `3`, `-3`, and `A` in the measurement showed that this is not always "rounding": `toInt` discards the decimal part, so `-3.99` becomes `-3`. `toIntOrNull` returns `null` for a string that cannot be converted, while `toInt` throws an exception in the same case, and the message `For input string: "x"` confirmed this behavior. When converting text from a user to a number, choosing the `OrNull` form makes the error a part of the flow.

Finally, the negative division and modulo measurement showed that the sign of the result follows the same rule as in Java: division rounds toward zero, and the remainder carries the sign of the dividend. The modulo in mathematics and the modulo operation in a program are not the same thing; in a circular index calculation that works with negative numbers, code that uses `%` instead of `mod` can push the index below zero.

#### The Bounding Measurement

For a boxed number, the criterion depends on the value: 1000 is content, 127 is singleton. For this reason, comparing the identity of numbers with `===` is unreliable; number comparison is done with `==`. The same value came out singleton at 127 and content at 1000. Instead of a single criterion, we saw two criteria.

NaN sets a separate limit: in a comparison whose static type is `Double`, NaN is not equal, and under the type `Any` it is equal. The meaning of the `==` operator changes at the point where the operator depends on the static type of the expression on the left.

#### Summary

- An integer constant is an `Int`; a value that exceeds the limit asks for a `Long`. A constant with a decimal point is a `Double`.
- An `Int` value is not assigned to a `Long` variable automatically, and an `Int` and a `Long` constant cannot be compared with `==`.
- Arithmetic can mix types: the sum of an `Int` and a `Double` is a `Double`.
- A nullable `Int` is boxed; 127 came out singleton and 1000 came out content.
- This lesson's criterion: **content** (1000); bounding measurement: the value 127 is a singleton on the JVM, and NaN gives different equality depending on the static type.

#### Next Step

We have seen the numbers and the character. In the next lesson we return to strings: templates, raw strings, and `trimIndent`. We will measure whether building a string with a template gives the same object as writing the same text by hand.

### String Templates

Source: https://academia.sh/en/courses/kotlin/string-templates

Introduces string templates, raw strings, and the `trimIndent` function; measures that a string built with a template falls under the content criterion, while a single-variable template stays a singleton.

In the number types, automatic boxing showed that the same value can split into two objects. Strings ask this question from another direction: when I build a text with a template, do I get the same object as the text I write by hand? First we look at the template itself: how it is written, how it combines with a raw string, and what it changes in the output. Then we run the same templates through the equality measurement and look for the place where a single way of writing falls under two criteria.

#### Template, Raw String, and trimIndent

In Kotlin, the `$` sign is used to place a value inside a double-quoted string. `$name` puts the value of a variable into the string, and `${expression}` puts in the result of an expression. The expression form can also be a function call or a property access: `${items.size}`, `${items.first().uppercase()}`. If you want to write the `$` character with its own meaning inside a string, you put a backslash in front of it: `\$5`.

A string that begins with three double quotes is a raw string. Line breaks and indentation are kept as they are and escape sequences do not work, but templates do. When a raw string spans several lines, the indentation at the start of each line becomes part of the text; the `trimIndent` function removes the common indentation and drops the empty first and last lines.

In this lesson too, the measurements are made with the helper file introduced in the first lesson. The `Criterion` class takes a value, its twin, and a different value, and asks three questions; the file is given below exactly as it was.

```kotlin
// Criterion.kt — classifies the criterion that makes two values equal
class Criterion(val label: String, first: Any?, twin: Any?, other: Any?) {
    val sameObject = first === twin
    val equalTwin = first == twin
    val equalOther = first == other

    val name: String = when {
        sameObject -> "singleton"
        !equalTwin -> "identity"
        !equalOther -> "content"
        else -> "partial-content"
    }

    fun report() {
        println("$label: same-object=$sameObject, twin=$equalTwin, other=$equalOther -> $name")
    }
}

fun summarize(checks: List<Criterion>) {
    for (name in listOf("singleton", "identity", "content", "partial-content")) {
        println("$name: ${checks.count { it.name == name }}")
    }
}
```

The program below runs these three things together.

```kotlin
// Main.kt — templates, raw string, and trimIndent
fun main() {
    val name = "Maya"
    val items = listOf("apple", "pear")
    println("Hello, $name")
    println("Item count: ${items.size}, first: ${items.first().uppercase()}")
    println("Price: \$5")
    val text = """
        name: $name
        items: ${items.joinToString()}
    """.trimIndent()
    println(text)
}
```

```
Hello, Maya
Item count: 2, first: APPLE
Price: $5
name: Maya
items: apple, pear
```

The first line places a single variable: `Hello, Maya`. The second places two expressions: `items.size` gives the length of the list, and `items.first().uppercase()` converts the first element to uppercase; `Item count: 2, first: APPLE` appears in the output. In the third line, the backslash saves the text `$5` from being read as a template, and `Price: $5` is printed. The raw string produces a two-line text from four lines of source code: the indentation is removed by `trimIndent`, and the lines `name: Maya` and `items: apple, pear` appear aligned in the output. `items.joinToString()` turns the list of elements into a single text, separated by commas.

The three forms of writing in the program do three separate jobs. The plain `$name` is the shortest form and puts in only the value of a name. The braced form is needed when the name alone is not enough, or when a letter follows the template immediately; the braces show clearly where the name ends. The raw string is useful in multi-line texts where escape sequences should be avoided, for example a configuration sample or a formatted message draft. `trimIndent` lets you keep the source code indented; the text in the output stays flush with the left edge.

**LB24.** Only simple expressions were measured inside a template; the behavior of a nested template, or of an expression that contains a string inside a string, is outside this lesson.

**LB25.** `trimIndent` removes the common indentation; what it does on lines with irregular indentation was not measured in this lesson.

#### The Criterion of a String Built with a Template

We return to the question of the lesson. In the earlier lesson, the same string constant became a single object in two variables and came out singleton. For a string built with a template we write three observations. `template-built`: `"lang: $name"` against `"lang: $name"`, that is, two separate template expressions. `single-variable-template`: only `"$name"`, a template with no other text, against the `name` variable. `template-and-suffix`: `"$name."`, that is, a variable plus a constant suffix.

In the second lesson we saw that strings built with `joinToString` and `buildString` came out as content. A template is also a way of writing that builds a string at run time; so we expect the same result. We measure the expectation, because how the compiler handles a template is not a language rule but an implementation detail, and a detail cannot be derived from the text. In each observation the twin is obtained by writing the same template a second time or by a constant; the different value carries the letter change `kotlim`.

```kotlin
// Main.kt — a string built with a template
fun main() {
    val name = "kotlin"
    val checks = listOf(
        Criterion("template-built", "lang: $name", "lang: $name", "lang: kotlim"),
        Criterion("single-variable-template", name, "$name", "kotlim"),
        Criterion("template-and-suffix", "$name.", "$name.", "kotlim."),
    )
    for (check in checks) {
        check.report()
    }
    summarize(checks)
}
```

```
template-built: same-object=false, twin=true, other=false -> content
single-variable-template: same-object=true, twin=true, other=false -> singleton
template-and-suffix: same-object=false, twin=true, other=false -> content
singleton: 1
identity: 0
content: 2
partial-content: 0
```

`template-built` is content. Two template expressions produce two separate strings; `same-object=false`, but they are equal by `==` and not equal to the different text. `template-and-suffix` is also content; a template that joins a variable with a constant suffix builds a new object every time it runs.

`single-variable-template`, on the other hand, is a singleton: when `"$name"` is written, the object obtained is the very object that the `name` variable points to, `same-object=true`. A template to which no other text is added does not build a new string.

The common point of these three observations is that all of them use the same `name` variable. The variable is single, and the template form changes. This choice is deliberate: we want to see that what affects the criterion is not the value of the variable but the form of the template. The period in `template-and-suffix` is a constant text that follows the name immediately; the template does not count this period as part of the name, it places only the `name` variable and leaves the period as it is.

**LB26.** These three observations were measured with Kotlin 2.4.21; that `"$s"` does not build a new object is the compiler's behavior in this version, and it is not claimed that it will be the same in other versions.

**LB27.** The `name` variable here is a `val` bound to a string constant; that the same result comes out when the variable is bound to a string built at run time was not measured.

This result suggests a habit when working with strings. When comparing one text with another for equality, there is no need to think about whether the text came from a constant or from a template; `==` looks at the text in both cases. Which object the text is only shows up when `===` is asked, and then the answer changes according to whether there is anything else inside the template.

#### When the Dollar Sign Starts a Template

Above we saw that the dollar sign can be escaped with `\$5`. But does every `$` ask for an escape? The block below prints seven ways of writing one after another: a `$` followed by a digit, a `$` surrounded by spaces, a `$` at the end of a string, two separate escape routes, and a period written right after a variable name.

```kotlin
// Main.kt — when the dollar sign starts a template
fun main() {
    val name = "kotlin"
    println("price: $5")
    println("a $ b")
    println("last: $")
    println("${'$'}name")
    println("\$name")
    println("$name.length")
    println("${name.length}")
}
```

```
price: $5
a $ b
last: $
$name
$name
kotlin.length
6
```

In the first three lines, `$` was printed as it is: `$5`, `a $ b`, and the `$` standing at the end. The rule is this: if the first letter of an identifier comes after the `$` sign, a template starts; if a digit, a space, or the end of the string comes, the `$` is an ordinary character. For this reason `"price: $5"` worked without asking for an escape. The fourth and fifth lines give two ways of writing a text that wants a `$` at the start of a name: `"${'$'}name"` and `"\$name"` print the same text `$name`.

The sixth line shows a frequent mistake. A person who writes `"$name.length"` expects 6, and `kotlin.length` appears on the screen: the plain `$name` form takes only the name, and what comes after the period stays as text. To reach the property, `"${name.length}"` is written, and the last line gives `6`. The reason for the mechanism is that the plain form reads only a name, not an expression; the braces mark the boundary of the expression explicitly.

Java comparison: in Java, `+` concatenation, `String.format`, or `formatted` is used to put a value into a string; `$` is not a special character. A person moving from Java to Kotlin may assume that they will always escape the `$` sign. This observation measures only the Kotlin side.

**LB28.** These seven ways of writing were measured with a single `name` variable; how names in which a Unicode letter follows the `$` are parsed was not measured in this lesson.

#### A String Is Immutable

In Kotlin, `String` is immutable: no method changes the content of an existing object. The methods that make a change return a new string. The block below first shows this with `uppercase`, then runs four methods through the equality measurement. The question is this: when a method changes nothing, does it build a new object, or does it return the one in hand?

```kotlin
// Main.kt — a string is immutable, methods may return a new object
fun main() {
    val text = "kotlin"
    val upper = text.uppercase()
    println(text)
    println(upper)
    val checks = listOf(
        Criterion("replace-no-match", text, text.replace("x", "y"), "kotlim"),
        Criterion("replace-match", text, text.replace("k", "k"), "kotlim"),
        Criterion("uppercase-lowercase", text, text.uppercase().lowercase(), "kotlim"),
        Criterion("trim-no-spaces", text, text.trim(), "kotlim"),
    )
    for (check in checks) {
        check.report()
    }
    summarize(checks)
}
```

```
kotlin
KOTLIN
replace-no-match: same-object=true, twin=true, other=false -> singleton
replace-match: same-object=false, twin=true, other=false -> content
uppercase-lowercase: same-object=false, twin=true, other=false -> content
trim-no-spaces: same-object=true, twin=true, other=false -> singleton
singleton: 2
identity: 0
content: 2
partial-content: 0
```

The first two lines confirm immutability. While `text` is still `kotlin`, `upper` carries the value `KOTLIN` as a separate object. The measurement lines, on the other hand, split into two criteria. `replace-no-match` is a singleton: `replace`, which tries to replace the text `"x"` with `"y"`, returns the same object when it finds no match. `trim-no-spaces` is also a singleton: for a string with no spaces to trim, `trim` returns its own object. `replace-match` is content: when a match is found, a new string is built even if the replacing text is the same. `uppercase-lowercase` is also content, because the two conversions produce successive new objects.

This extends the rule we saw in the second lesson: what determines the criterion is where the value comes from. Here the source is a method, and when the method has no work to do, it returns its input. This behavior is not a guarantee of the language but a measured result; code that tests with `===` is fragile for this reason. In Java, `String` is also immutable and methods similarly return new strings, but the Java side was not measured in this section.

**LB29.** Four methods were measured with a single string; that the same "no work, same object" behavior holds for `trimStart`, `trimEnd`, and `substring`, other than `trim`, was not measured in this lesson.

#### Length, Escape, and UTF-16

`length` gives the number of characters in a string, but the word "character" is tricky here. The block below compares a string with an escape sequence against its raw counterpart, then asks the length and the code point count of an emoji character. At the end, the trimming of a raw string from the line starts with `trimMargin` is shown.

```kotlin
// Main.kt — length, escape sequence, and UTF-16
fun main() {
    val escaped = "a\nb"
    val raw = """a\nb"""
    println(escaped.length)
    println(raw.length)
    val face = "\uD83D\uDE00"
    println(face.length)
    println(face.codePointCount(0, face.length))
    val margin = """
        |line one
        |line two
    """.trimMargin()
    println(margin.lines().size)
    println(margin.lines().last())
}
```

```
3
4
2
1
2
line two
```

The length of the string `"a\nb"` comes out as `3`: `\n` is a single line-break character. The same writing gives `4` in a raw string, because the escape does not work in a raw string and the backslash and the `n` are counted as two separate characters. Whether the escape works can be read from these two numbers.

For the emoji, `length` gives `2` and `codePointCount` gives `1`. Kotlin strings are held in UTF-16 encoding, and this character needs two code units (a surrogate pair). `length` counts code units, not code points. The frequent mistake is to use `length` for a "character count" shown to the user; in a text that contains emoji, the number comes out wrong. In Java, `String.length()` gives the same code unit count; this comes from the JVM string being under the Kotlin string.

The last two lines are the result of `trimMargin`. Two lines remain from the two lines that begin with the `|` sign, and the second reads as `line two`. `trimIndent` removes the common indentation, while `trimMargin` drops, on each line, the part up to the `|` sign; in a text with irregular indentation the second is more predictable. This comparison was measured only with this two-line example.

**LB30.** The length measurement was made with one emoji and one escape sequence; what the `length` result is for combining characters (for example, a letter with a separate accent mark) was not measured in this lesson.

#### Habits That Come from the Measurements

The most frequent mistake in writing templates is to confuse the plain `$name` form with writing an expression. The rule is simple: if you need a property, a method, or any operation, you use braces. In the measurement the output of `"$name.length"` was `kotlin.length`; this mistake is not caught at compile time, because the syntax is valid and the result only looks wrong. Mistakes of this kind are noticed not in tests but by looking at the output. For this reason, when writing messages with templates, it should become a habit that everything that comes after a period is inside the braces.

The immutability of strings has two consequences. The first is that forgetting to assign the result of a method such as `uppercase` to a variable means the change is lost: code that writes the line `text.uppercase()` alone does nothing. The second is that growing a string with `+` in a loop produces a new object at every step; for multi-step concatenation, `StringBuilder` or `buildString` is used. The same rule holds in Java, and Kotlin does not bring a separate guarantee here.

On length, one should not trust the word "character". `length` counts UTF-16 code units; for this reason emoji and characters in some writing systems count as two units. A form that shows the user a limit of "at most 20 characters" and checks this number with `length` applies the limit wrongly for someone who uses emoji. If the code point count is needed, `codePointCount` is used; the count of characters as the user perceives them (graphemes) needs a separate library and was not measured in this lesson.

In raw strings escapes do not work; for this reason they are suitable for file paths, regular expressions, and multi-line texts, but a person who writes `\n` does not get a line break. In the measurement the same writing gave four characters in a raw string and three in an escaped string. Being able to read from the number which one was used is useful when debugging.

#### The Bounding Measurement

The criterion of a string built with a template is content, but this generalization leaves the single-variable template outside. When `"$name"` places only a variable, it came out singleton; the moment text or a suffix is added, it turned to content. The same form of writing, that is, a template, can fall under two criteria, and the distinction depends on whether there is anything else inside the template.

A rule can be drawn from here: use `==` instead of comparing strings with `===`. The bounding measurement shows the reason for this rule: the same template form can give a singleton or content; `==` gives the right answer in both cases.

#### Summary

- `$name` and `${expression}` place a value into a string; to write the `$` character in a string, `\$` is used.
- The triple-quoted raw string keeps line breaks; `trimIndent` removes the common indentation.
- Two separate template expressions are separate objects even if they produce the same text.
- The `"$name"` template, which places only a variable, gives the variable's own object in Kotlin 2.4.21.
- This lesson's criterion: **content**; bounding measurement: a single-variable template is a singleton.

#### Next Step

We have seen how strings are built. In the next lesson we move on to the flow of the program: `if` and `when` expressions, `for` loops, and ranges. While `when` matches a value to a branch, it uses `==`; we measure which example fails to hit the branch. In that lesson the identity criterion will meet us again, and we will see that the `is` check does not ask the same question as `==`.

### Control Flow

Source: https://academia.sh/en/courses/kotlin/control-flow

Introduces the `if` and `when` expressions, the `is` check, and the `for` loop; measures that a `when` branch matches with `==` and does not hit for an ordinary class instance.

In string templates we saw that the same text can be built in two ways. In this lesson we move on to flow: choosing a value according to a condition, distributing a value to branches, walking over a range. At the end we find the identity criterion again: `when` uses `==` while matching a value against a branch, and an instance with no equality cannot hit the branch.

#### if, when, and for

In Kotlin, `if` is an expression: it produces a value, and that value can be assigned to a variable. A separate ternary operator is not needed; you write `val label = if (score >= 50) "passed" else "failed"`. `when` is also an expression. In the form without a subject, every branch carries a logical expression and the first true branch runs. In the form tied to a value, you write `when (value)`; each branch begins either with a value or with a type check using `is`. When `when` is used as an expression, the compiler wants to be sure that all cases are covered: in the form without a subject an `else` branch is needed; in the form with a subject, if `Boolean`, an enumeration, or a sealed class is listed with all of its cases, `else` is not required.

The `for` loop walks over a range or a collection. The range `1..3` is a closed range: the upper bound is included. `1 until 7` leaves the upper bound out. `step` sets the step, and `3 downTo 1` counts backward.

In this lesson too, the measurements are made with the helper file introduced in the first lesson. The `Criterion` class takes a value, its twin, and a different value, and asks three questions; the file is given below exactly as it was.

```kotlin
// Criterion.kt — classifies the criterion that makes two values equal
class Criterion(val label: String, first: Any?, twin: Any?, other: Any?) {
    val sameObject = first === twin
    val equalTwin = first == twin
    val equalOther = first == other

    val name: String = when {
        sameObject -> "singleton"
        !equalTwin -> "identity"
        !equalOther -> "content"
        else -> "partial-content"
    }

    fun report() {
        println("$label: same-object=$sameObject, twin=$equalTwin, other=$equalOther -> $name")
    }
}

fun summarize(checks: List<Criterion>) {
    for (name in listOf("singleton", "identity", "content", "partial-content")) {
        println("$name: ${checks.count { it.name == name }}")
    }
}
```

The program below runs these together. `grade` turns a score into a word with a `when` that has no subject. `describe` separates an `Any` value by its type with `is`: for an `Int` it states the double, and for a `String` it states the length.

```kotlin
// Main.kt — if, when, is, and for
fun grade(score: Int): String = when {
    score >= 90 -> "excellent"
    score >= 70 -> "good"
    else -> "pass"
}

fun describe(value: Any): String = when (value) {
    is Int -> "integer, double is ${value * 2}"
    is String -> "string, length ${value.length}"
    else -> "unknown"
}

fun main() {
    val score = 82
    val label = if (score >= 50) "passed" else "failed"
    println("$label, ${grade(score)}")
    println(describe(21))
    println(describe("kotlin"))
    println((1..3).joinToString(" "))
    println((1 until 7 step 2).joinToString(" "))
    println((3 downTo 1).joinToString(" "))
}
```

```
passed, good
integer, double is 42
string, length 6
1 2 3
1 3 5
3 2 1
```

In the first line, the `label` variable took the value `passed` through the `if` expression, and the call `grade(82)` returned `good`, because 82 is below ninety and above seventy. The second and third lines show the two branches of the `describe` function: because `21` is an `Int`, its double, 42, was printed; because `"kotlin"` is a `String`, its length, 6, was printed. Inside these branches, the `value` variable was reached with the new form of its type; this feature is called a smart cast. The last three lines are ranges: `1..3` gives all three, `1 until 7 step 2` gives the values one, three, five, and `3 downTo 1` counts backward.

**LB31.** The `when` expression was measured with integer and string branches; the exhaustive `when` over an enumeration and over `Boolean` is measured below, and the check over a sealed class is outside this lesson.

**LB32.** Ranges were printed only with `joinToString`; what an empty range does in a loop is measured separately below.

#### A when Branch Matches with ==

The measurement of the lesson looks at the matching rule of the `when` branch. When a branch expression is written inside `when (value)`, Kotlin makes the comparison `value == branch`. For this reason, whether a branch hits depends on the equality criterion. The `branchMatches` function asks this directly: it matches the `value` it receives against `branch` through `when` and returns `true` or `false`.

Three cases are tried. The first is `Point(1, 2)` against `Point(1, 2)`: a data class, that is, the content criterion. The second is `Box(1)` against `Box(1)`: an ordinary class, that is, identity. The third is the same `box` instance matched against itself. Then the `is Box` branch is given to the same `Box(1)` value. Finally, `Criterion` classifies three observations.

```kotlin
// Main.kt — a when branch matches with ==
class Box(val size: Int)

data class Point(val x: Int, val y: Int)

fun branchMatches(value: Any, branch: Any): Boolean = when (value) {
    branch -> true
    else -> false
}

fun main() {
    val box = Box(1)
    println(branchMatches(Point(1, 2), Point(1, 2)))
    println(branchMatches(Box(1), Box(1)))
    println(branchMatches(box, box))
    println(when (Box(1)) {
        is Box -> "is Box branch hits"
        else -> "does not hit"
    })
    val checks = listOf(
        Criterion("when-branch-box", Box(1), Box(1), Box(2)),
        Criterion("when-branch-point", Point(1, 2), Point(1, 2), Point(1, 3)),
        Criterion("range", 1..3, 1..3, 1..4),
    )
    for (check in checks) {
        check.report()
    }
    summarize(checks)
}
```

```
true
false
true
is Box branch hits
when-branch-box: same-object=false, twin=false, other=false -> identity
when-branch-point: same-object=false, twin=true, other=false -> content
range: same-object=false, twin=true, other=false -> content
singleton: 0
identity: 1
content: 2
partial-content: 0
```

`branchMatches(Point(1, 2), Point(1, 2))` gave `true`: in a data class `==` looks at the content, and the branch hit. `branchMatches(Box(1), Box(1))` gave `false`: even though the two `Box(1)` instances carry the same constructor argument, they are separate objects, and in an ordinary class `==` looks at identity. The same `box` instance matched itself, because an object is equal to itself. The `is Box` branch looks at the type, not the value: even a freshly built `Box(1)` hits the `is Box` branch. For this reason the line `is Box branch hits` was printed.

The three classification lines summarize this. `when-branch-box` is identity: `==` with the twin gives `false`. `when-branch-point` is content: `==` with the twin gives `true`, and with the different value gives `false`. `range` is also content: the range `1..3` is equal to another separately built `1..3` range and is not equal to the `1..4` range. The summary lines show zero singleton, one identity, two content, and zero partial content.

**LB33.** The `branchMatches` function takes the branch as a variable; that the same `==` rule holds when a constant branch is written inside `when` was not measured separately in this lesson.

**LB34.** Range equality was measured with `IntRange`; the same behavior is not claimed for `LongRange` and `CharRange`.

#### when with a Range, a Multi-Value Branch, and an Enumeration

`when` branches are not limited to a single value. The block below asks three things: in which order a comma-separated multi-value branch, a range branch with `in`, and a branch that catches the outside of the range are evaluated when used together; whether a `when` over an enumeration (enum) compiles without `else`; and whether a `when` over `Boolean` closes with two values.

```kotlin
// Main.kt — when: multi-value branch, range, and enumeration
enum class Level { LOW, MEDIUM, HIGH }

fun classify(n: Int): String = when (n) {
    0 -> "zero"
    1, 2, 3 -> "few"
    in 4..9 -> "medium"
    !in 0..99 -> "out of range"
    else -> "many"
}

fun weight(level: Level): Int = when (level) {
    Level.LOW -> 1
    Level.MEDIUM -> 5
    Level.HIGH -> 10
}

fun main() {
    println(listOf(0, 2, 7, 50, 120).map { classify(it) })
    println(Level.entries.map { weight(it) })
    val flag = true
    println(when (flag) {
        true -> "yes"
        false -> "no"
    })
}
```

```
[zero, few, medium, many, out of range]
[1, 5, 10]
yes
```

For the inputs 0, 2, 7, 50, and 120 the result was `zero`, `few`, `medium`, `many`, `out of range`. The value 2 fell on the branch `1, 2, 3`, and 7 on the branch `in 4..9`. The value 50 did not hit the out-of-range branch and went to the `else` branch, while 120 hit the out-of-range branch. The rule is this: branches are tested from top to bottom and the first one that hits runs. Because the out-of-range branch was written before the `else` branch, the value 120 reached it; had the `else` branch been written earlier, the `out of range` branch could never have been reached; this reversed order could not be measured because it does not compile.

The second line gave `[1, 5, 10]`. The `weight` function carries no `else` branch and still compiles: the compiler sees that the branches cover all three values of the `Level` enumeration. The frequent mistake is to add a new value to the enumeration and forget to update the `when` expressions; an expression without an `else` branch gives a compile error in this case and finds the missing branch. If `else` is written, this guarantee is lost. This holds because `when` is used as an expression.

The third line gave `yes`: because `Boolean` is a type with two values, the `true` and `false` branches are exhaustive without `else`.

**LB35.** Exhaustiveness was measured with `enum class` and `Boolean`; the check of a `when` over a sealed class was not measured in this lesson.

#### Empty Range, Step, and Labeled break

Ranges look easy and produce mistakes because of their edge cases. The block below counts an empty range, builds a `downTo` and a `..` that use `step`, tries a zero step, and leaves a nested loop with a labeled `break`.

```kotlin
// Main.kt — empty range, step, and labeled break
fun main() {
    println((5..1).count())
    println((1..0).toList())
    println((10 downTo 1 step 3).toList())
    println((1..10 step 4).last)
    try {
        println((1..3 step 0).toList())
    } catch (e: IllegalArgumentException) {
        println(e.message)
    }
    var visited = 0
    outer@ for (row in 1..3) {
        for (col in 1..3) {
            if (row * col == 4) break@outer
            visited++
        }
    }
    println(visited)
}
```

```
0
[]
[10, 7, 4, 1]
9
Step must be positive, was: 0.
4
```

`(5..1).count()` gave `0` and `(1..0).toList()` gave an empty list: the `..` operator works from smaller to larger, and if the start is larger than the end the range is empty and the loop never runs. If you need to count backward, `downTo` must be written. This is the frequent mistake: a person who writes `for (i in 5..1)` expects a countdown, and the program silently does nothing.

The list `10 downTo 1 step 3` became `[10, 7, 4, 1]`. The `last` value of the range `1..10 step 4` came out as `9`, that is, not 10: if the step does not fit the upper bound exactly, the last value stays below the bound. A zero step threw an `IllegalArgumentException` with the message `Step must be positive, was: 0.`; the step has to be positive, and counting backward is done with `downTo`, not with a negative step.

The last line gave `4`. In the inner loop, when `row * col == 4`, `break@outer` ended both the inner and the outer loop. A `break` without a label would have ended only the innermost loop. The visit count is four in total: three in row 1, one in row 2, since the break happens at the second column of row 2. In Java, a labeled `break` works the same way; the syntax is `outer:` with `break outer;`.

**LB36.** The range measurements were made with `Int`; step and overflow behavior in a `Long` range was not measured in this lesson.

#### try Is an Expression

In Kotlin, `try` also produces a value. The block below makes the value of a `try` expression the return of a function, looks at when the `finally` block runs, and uses a function call inside an `if` expression. Finally, `firstOrNull` and the elvis operator are tried together.

```kotlin
// Main.kt — try is an expression
fun parse(text: String): Int = try {
    text.toInt()
} catch (e: NumberFormatException) {
    -1
} finally {
    println("tried: $text")
}

fun main() {
    println(parse("42"))
    println(parse("kotlin"))
    val label = if (parse("7") > 5) "big" else "small"
    println(label)
    val firstNegative = listOf(3, -2, 5).firstOrNull { it < 0 } ?: 0
    println(firstNegative)
}
```

```
tried: 42
42
tried: kotlin
-1
tried: 7
big
-2
```

The first call, `parse("42")`, printed first the line `tried: 42`, which is the output of the `finally` block, and then the return value `42`: `finally` runs before the value reaches the caller. In the second call, because `"kotlin"` could not be converted to a number, the `catch` branch ran and returned `-1`; again `tried: kotlin` was printed first. In the third line, the call `parse("7")` was used inside `if`: `tried: 7` was printed, and because 7 is greater than 5, `big` was written.

The frequent mistake is to put an expression that returns a value in the `finally` block and to assume that it will change the value of the try expression. Here, even though `println` is the last expression, the value of the `finally` block does not join the value of the expression; the returned value is the last expression of the `try` or `catch` block. The last line gave `-2`: the first element in the list that satisfied the condition `it < 0` was `-2`; if there had been none, the elvis operator would have returned `0`, but this branch was not measured. In Java, `try` is a statement and does not produce a value.

**LB37.** The `try` measurements were made with `NumberFormatException`; how the value is overridden when a `return` is written in the `finally` block was not measured in this lesson.

#### Habits That Come from the Measurements

The common lesson of the measurements in flow control is that edge cases are silent. An empty range does not give an error; it only does nothing. If the step value does not fit the bound exactly, as in `1..10 step 4`, the last value stays below the bound. A zero step, on the other hand, raises an exception with a single message. Of these three cases, only the last appears as an error; the others produce wrong output.

The absence of an `else` branch in a `when` expression is a design tool. If you do not write `else` on an enumeration, the compiler directs you to all the `when` expressions when a new value is added. If you write `else`, you have turned off this warning yourself. For this reason the `else` branch should be used where it really means "everything else", for example in integer ranges; it should not be used on a closed set of values. Order also needs attention: because branches are tested from top to bottom, the specific branch is written before the general one, and in the measurement the value 120 reached the out-of-range branch because it was before `else`.

In a `try` expression, the value of the `finally` block does not join the result, but its side effect always runs. Work such as closing a resource and writing a log is done in `finally` for this reason, while the calculation of the value is done in `try` and `catch`. In Java, `try` is a statement; to assign the value to a variable, one has to define a variable outside. In Kotlin, writing `val result = try { ... } catch ...` makes this variable a read-only `val` and gives shorter code.

A labeled `break` is the most readable way to leave nested loops, but its use should be kept limited. In most cases, moving the search into a separate function and leaving with `return` requires no label and is also easy to test.

#### The Bounding Measurement

The identity criterion holds only for branches matched with `==`. The `is Box` branch hits, because a type check is not a question of equality. That is, two branches can stand side by side in the same `when`: one does not hit because of identity, and the other hits because of the type. A program that builds a second `Box(1)` instance and tries to match it inside `when` can never hit the branch unless `Box` is a data class; but when `is Box` is written, the branch hits.

The second limit is in ranges: `1..3` does not fall under the identity criterion. Two range objects are equal even if they were built separately. The equality of a range and the hitting of an `in` branch are different questions: the first was measured in this section, and the second in the `when` section.

#### Summary

- `if` and `when` are expressions that produce a value; when `when` is used as an expression, all cases must be covered, which an `else` branch or the listing of all cases of an enumeration, `Boolean`, or sealed class subject provides.
- `is` is a type check and allows the value to be used inside the branch as a variable of that type.
- `..`, `until`, `downTo`, and `step` build ranges; `for` walks over these ranges.
- A branch inside `when (value)` matches with `value == branch`; because an ordinary class instance rests on identity, it cannot hit the branch.
- This lesson's criterion: **identity**; bounding measurement: the `is Box` branch hits and two `1..3` ranges fall under the content criterion.

#### Next Step

We have seen the flow; now we move to the tool for packaging the flow: functions. We begin with parameters with default values, named arguments, and `vararg`. We measure whether taking the same function twice as a reference gives the same object or only equal content.

### Functions

Source: https://academia.sh/en/courses/kotlin/functions

Introduces parameters with default values, named arguments, and the `vararg` parameter; measures that a function reference gives a separate but equal object each time it is taken.

In control flow we saw how to distribute a value to branches and how to walk over a range. In this lesson we package that flow into named, reusable pieces: functions. First we look at how the parameters of a function can be made flexible, then we ask the same question of the functions themselves: when we take the same function twice as a value, do we get two equal objects, or a single object?

#### Making Parameters Flexible

In Kotlin, a function is declared with the `fun` keyword: the name, then the parameters inside parentheses in the form `name: Type`, then a colon and the return type. If the body is a single expression, it can be written with `=`. Three features make the parameter list flexible.

The first is the default value: if `= value` is added to a parameter, the caller can leave it out. The second is the named argument: if `punctuation = "?"` is written in the call, the value is bound by name, not by position, and the parameters in between stay at their defaults. The third is the `vararg` parameter: if a parameter is marked `vararg`, the caller can give as many values as they want, and inside the function these appear as an array. To pass an array you already have to a `vararg` parameter, a `*` is placed in front of the array.

In this lesson too, the measurements are made with the helper file introduced in the first lesson. The `Criterion` class takes a value, its twin, and a different value, and asks three questions; the file is given below exactly as it was.

```kotlin
// Criterion.kt — classifies the criterion that makes two values equal
class Criterion(val label: String, first: Any?, twin: Any?, other: Any?) {
    val sameObject = first === twin
    val equalTwin = first == twin
    val equalOther = first == other

    val name: String = when {
        sameObject -> "singleton"
        !equalTwin -> "identity"
        !equalOther -> "content"
        else -> "partial-content"
    }

    fun report() {
        println("$label: same-object=$sameObject, twin=$equalTwin, other=$equalOther -> $name")
    }
}

fun summarize(checks: List<Criterion>) {
    for (name in listOf("singleton", "identity", "content", "partial-content")) {
        println("$name: ${checks.count { it.name == name }}")
    }
}
```

A default value does not have to be a constant; it can be any expression. When the caller leaves the parameter out, that expression is computed at the moment of the call, so two calls of the same function can get two separate results from the default. A named argument is also not only a convenience of order: someone reading the call understands what the value belongs to from the writing `punctuation = "?"`, without looking at the declaration of the function. In the second section we will measure this behavior of the default value.

The program below uses the three features together. `greet` carries two parameters with default values, and `sum` takes a `vararg`.

```kotlin
// Main.kt — default value, named argument, and vararg
fun greet(name: String, greeting: String = "Hello", punctuation: String = "."): String =
    "$greeting, $name$punctuation"

fun sum(vararg numbers: Int): Int = numbers.sum()

fun main() {
    println(greet("Ada"))
    println(greet("Ada", punctuation = "?"))
    println(greet(greeting = "Hi", name = "Sam"))
    println(sum())
    println(sum(1, 2, 3))
    val values = intArrayOf(4, 5)
    println(sum(*values, 6))
}
```

```
Hello, Ada.
Hello, Ada?
Hi, Sam.
0
6
15
```

The first two lines show calls in which the name is given alone and in which the punctuation is changed with a named argument: when `greeting` and `punctuation` were left out, `Hello` and the period were used, and when `punctuation = "?"` was written, only the punctuation changed. In the third line the arguments were given out of order, by their names, and `Hi, Sam.` came out. The last three lines are for `vararg`: when no value was given, the total was `0`, and with three values it was `6`; `sum(*values, 6)` added the two elements of the array and the extra six together and gave `15`.

**LB38.** Default values and `vararg` were measured only with `String` and `Int` parameters; the same behavior for parameters whose type is a class was not tested separately in this lesson.

**LB39.** Reordering with named arguments was shown in only one call; the rules for mixing named and positional arguments in the same call are outside this lesson.

#### Function Reference and Default Value

The measurement of the lesson looks at two things. The first is the function reference: the writing `::plain` gives the `plain` function as a value without calling it. Does using the same writing twice give the same object, or separate objects? The second is the default value: does the default `into: MutableList<Int> = mutableListOf()` build a new list at every call, or share a single list?

`collectMutable` adds an item to the list it receives and returns the list; if no list is given, the default empty mutable list is used. `defaultList` returns the `listOf()` default directly. We write three observations: `function-reference`, two `::plain` and, as the different value, `::other`; `default-mutableListOf`, `collectMutable(1)` twice and `collectMutable(2)` as the different value; `default-listOf`, `defaultList()` twice and `listOf(1)` as the different value.

```kotlin
// Main.kt — function reference and default values
fun plain(n: Int): Int = n

fun other(n: Int): Int = n + 1

fun collectMutable(item: Int, into: MutableList<Int> = mutableListOf()): MutableList<Int> {
    into.add(item)
    return into
}

fun defaultList(into: List<Int> = listOf()): List<Int> = into

fun main() {
    val checks = listOf(
        Criterion("function-reference", ::plain, ::plain, ::other),
        Criterion("default-mutableListOf", collectMutable(1), collectMutable(1), collectMutable(2)),
        Criterion("default-listOf", defaultList(), defaultList(), listOf(1)),
    )
    for (check in checks) {
        check.report()
    }
    summarize(checks)
}
```

```
function-reference: same-object=false, twin=true, other=false -> content
default-mutableListOf: same-object=false, twin=true, other=false -> content
default-listOf: same-object=true, twin=true, other=false -> singleton
singleton: 1
identity: 0
content: 2
partial-content: 0
```

The `function-reference` line shows `same-object=false` and `twin=true`: the two `::plain` writings gave separate objects, but these two objects came out equal by `==`. Separate objects, equal content: the content criterion. `default-mutableListOf` is also content. The two calls built two separate lists, and since both carry `[1]`, they are equal. The default value is computed again every time the function is called; an item added in one call does not leak into the next call.

`default-listOf`, on the other hand, is a singleton: `same-object=true`. The empty `listOf()` call gave the same object both times. The summary lines show one singleton, zero identity, two content, and zero partial content.

**LB40.** The equality of function references was measured only with top-level functions; how a reference bound to an object (`object::name`) is made equal was not measured in this lesson.

**LB41.** That the empty `listOf()` call gives a single object was measured on the JVM target of Kotlin 2.4.21; since this behavior depends on the implementation of the list, the same is not claimed for other versions.

#### The Default Value Is Computed at Every Call

We saw that a default value can be an expression. The real question is when the expression runs: once, when the function is defined, or at every call? The block below takes the default value from a function that increments a counter, and also tries whether the default of a parameter can depend on the one before it.

```kotlin
// Main.kt — the default value is computed at every call
var counter = 0

fun nextId(): Int {
    counter++
    return counter
}

fun label(text: String, id: Int = nextId(), width: Int = text.length): String =
    "$text#$id/$width"

fun main() {
    println(label("ab"))
    println(label("abc"))
    println(label("abc", id = 100))
    println(label("abc", width = 1))
    println(counter)
}
```

```
ab#1/2
abc#2/3
abc#100/3
abc#3/1
3
```

The first call gave `ab#1/2`: `nextId()` ran for `id` and produced 1, and since the `width` default is `text.length`, it became 2. The second call gave `abc#2/3`; the counter was incremented a second time. So the default value is computed again at every call. In the third call `id` was given explicitly as 100 and `abc#100/3` was printed; in this case `nextId()` did not run at all. The fourth call gave `abc#3/1`: `width` became 1 through the named argument, the counter ran again for `id`, and produced 3. The last line gave the value of `counter` as `3`: of the four calls, only three used the default `id`.

This proves two things. First, the default expression runs at the moment of the call; if it has a side effect, it repeats at every call. Second, defaults can reach the parameters before them (`width` looks at `text`). The frequent mistake is to assume that the default value is frozen at definition; in Python, the evaluation of a mutable default value once at definition is a known trap, and there is no such trap in Kotlin. Java has no default parameters; the same effect is obtained with overloading.

**LB42.** The default value measurement was made with a top-level counter; the behavior of the counter in a multi-threaded environment was not measured in this lesson.

#### Unit, Nothing, and a Local Function

A function whose return value is not written actually returns something. The block below prints a function that returns `Unit`, catches a function that returns `Nothing` inside a `try`, and uses a function placed inside another function.

```kotlin
// Main.kt — Unit, Nothing, and a local function
fun nothingToReturn() {
}

fun fail(message: String): Nothing = throw IllegalStateException(message)

fun total(values: List<Int>): Int {
    fun square(n: Int) = n * n
    var sum = 0
    for (v in values) {
        sum += square(v)
    }
    return sum
}

fun main() {
    println(nothingToReturn())
    println(nothingToReturn() === Unit)
    println(total(listOf(1, 2, 3)))
    val result = try {
        fail("stopped")
    } catch (e: IllegalStateException) {
        e.message
    }
    println(result)
}
```

```
kotlin.Unit
true
14
stopped
```

The first line gave `kotlin.Unit`: the function that we thought returned nothing actually returns the singleton object named `Unit`, and `println` can print it. The `true` in the second line shows that the comparison `nothingToReturn() === Unit` is true: `Unit` is a single object. This is a basic difference from `void` in Java; `void` is not a value, while `Unit` is a real value, and for this reason `Unit` can be used in generic types.

The third line is `14`, that is, the sum of 1, 4, and 9. The `square` function is defined inside `total` and can be called only from there. A local function can reach the variables of the outer function; this was not used here and the access was not measured.

The last line gave `stopped`. The return type of the `fail` function is `Nothing`: it never returns normally, it always throws an exception. The compiler knows this; since the `fail(...)` call had to produce a value inside `try`, the message of the caught exception became the result. The frequent mistake is to confuse the `Nothing` type with `Unit`. `Unit` returns and execution continues, while `Nothing` never returns.

**LB43.** The `Nothing` measurement was made with a function that throws an exception; marking functions that call an infinite loop or `exitProcess` with `Nothing` was not measured in this lesson.

#### Overload Selection and an Extension Function

Functions that carry the same name are chosen according to the parameter type. The block below defines four `show` functions and looks at which one is chosen with five different calls. Then it writes extension functions on an `Int` and a `String`, and shows how a `MutableList` parameter affects the calling side.

```kotlin
// Main.kt — overload selection, extension function, and parameter
fun show(x: Int) = "Int"
fun show(x: Long) = "Long"
fun show(x: Any) = "Any"
fun show(x: String) = "String"

fun Int.isEven(): Boolean = this % 2 == 0

fun String.initials(): String = split(" ").joinToString("") { it.take(1).uppercase() }

fun modify(list: MutableList<Int>) {
    list.add(99)
}

fun main() {
    println(show(1))
    println(show(1L))
    println(show("a"))
    val any: Any = "a"
    println(show(any))
    println(show(1.5))
    println(4.isEven())
    println("ada lovelace".initials())
    val nums = mutableListOf(1)
    modify(nums)
    println(nums)
}
```

```
Int
Long
String
Any
Any
true
AL
[1, 99]
```

`show(1)` gave `Int`, `show(1L)` gave `Long`, and `show("a")` gave `String`: the compiler chooses the most specific match. The fourth line is interesting. The `any` variable carries a `String` at run time, but because its type was declared as `Any`, `show(any)` chose the `Any` version. Overload resolution is made at compile time, according to the declared type of the variable; the run-time type does not affect the choice. In Java too, overloading is resolved at compile time, while a virtual method call is resolved according to the run-time type, and the two must not be confused. `show(1.5)` gave `Any`, because it fits only the `Any` version.

`4.isEven()` gave `true` and `"ada lovelace".initials()` gave `AL`: an extension function adds a new form of call to an existing type without changing the type itself. An extension function is resolved statically; it is not a real member of a class.

The last line gave `[1, 99]`. The `modify` function added to the list it received as a parameter, and the calling side saw this. In Kotlin, the reference itself is copied as a value: the object is shared, but the function cannot bind the caller's variable to another object; the parameter variable cannot be reassigned anyway. The frequent mistake is to make a change inside a collection passed to a function and assume that it will stay local.

**LB44.** Overload selection was measured with four definitions and five calls; which version `Short` or `Byte` arguments go to, besides `Int` and `Long`, was not measured in this lesson.

#### Habits That Come from the Measurements

The first thing the measurements show in a function definition is that the default value is an expression and runs again at every call. This removes the mutable-default trap of Python in Kotlin, but it creates another risk: if the default expression has a side effect, the number of calls affects the behavior of the program. In the measurement the `counter` value became three after four calls, because the default value was used in only three calls. Keeping default expressions free of side effects is a good habit for this reason; work such as incrementing a counter should be done in the body as an explicit call.

The distinction between `Unit` and `Nothing` is a useful part of the type system. We measured that a function returning `Unit` returns a singleton value; for this reason `Unit` is used in generic types without trouble. `Nothing` is the type of an expression that never returns, and in this way a writing such as `val x = y ?: fail("missing")` keeps `x` in the non-null type. The compiler knows that the code after the `fail` function will not run, so it can give an unreachable-code warning.

In overloading, the choice is made at compile time and looks at the declared type. Although the `any` variable carried a `String`, the `Any` version was chosen. This behavior is different from a virtual method call being resolved according to the run-time type, and code that confuses the two calls an unexpected version. As an overload set grows, it becomes harder to follow which call goes to which version; in that case separately named functions (`showInt`, `showLong`) can be more readable.

Extension functions add a form of call without changing the type. When you want to add a method to a library class, writing an extension is often enough instead of creating a subclass. But an extension is not a real member; it cannot reach the private fields of the class and is resolved statically, that is, it is not chosen according to the run-time type.

A mutable collection passed as a parameter changes on the calling side as well. If you do not want this, take the function parameter in the read-only `List` type or make a copy of it. The `[1, 99]` output in the measurement is a short proof of this trap.

#### The Bounding Measurement

The content criterion holds for the function reference, but one should not read the fact that the reference is a separate object each time it is taken as a contract: what was measured is that the two takings come out equal by `==`. In the same lesson the `listOf()` default came out singleton. That is, the writing of a default value alone does not determine the criterion: `mutableListOf()` is content because it builds a new object at every call, and `listOf()` is a singleton because it returns the same empty list. When looking at the default of a parameter, the criterion depends on what that expression builds.

#### Summary

- In a function declared with `fun`, parameters with default values, named arguments, and `vararg` make the call flexible; `*` spreads an array into a `vararg` parameter.
- The default value is computed again at every call; the `mutableListOf()` default is not shared between calls.
- Two function references such as `::plain` are separate objects but equal by `==`: content.
- The empty `listOf()` default gave the same object in two calls: singleton.
- This lesson's criterion: **content**; bounding measurement: the `listOf()` default is a singleton, and the `mutableListOf()` default is content.

#### Next Step

We have seen how to take and pass functions as a value. In the next lesson we move on to writing a function in place: lambdas and higher-order functions. There we ask the same question for two lambdas: are two lambdas the same object, equal, or neither?

### Lambdas and Higher-Order Functions

Source: https://academia.sh/en/courses/kotlin/lambdas-and-higher-order-functions

Introduces lambda syntax, function types, and functions that take functions; measures that two lambda expressions are not equal, while a lambda that captures nothing is a single object.

In the previous lesson we saw that a function reference such as `::plain` gives a separate but equal object each time it is taken. Now we write the function in place. A lambda is a function value without a name; it can be assigned to a variable, given to a function as an argument, or returned from a function. The question of this lesson continues the question of the previous one: are two lambdas with the same body equal?

#### Lambda and Function Type

A lambda is written inside curly braces: first the parameters, then `->`, then the body. In a lambda with a single parameter whose type is known from the context, the parameter is not declared; the implicit name `it` is used in its place. A function type has the form `(Int) -> Int`: the parameter types are inside parentheses, and the return type follows the arrow.

A function that takes a function or returns a function is called a higher-order function. When a function whose last parameter has a function type is called, the lambda can be written outside the parentheses: this is called a trailing lambda. The `inline` keyword appears in this lesson only as a definition: the body of a higher-order function marked `inline`, and the lambda given to it, are placed at the call site at compile time. `inline` was not measured in this lesson.

In this lesson too, the measurements are made with the helper file introduced in the first lesson. The `Criterion` class takes a value, its twin, and a different value, and asks three questions; the file is given below exactly as it was.

```kotlin
// Criterion.kt — classifies the criterion that makes two values equal
class Criterion(val label: String, first: Any?, twin: Any?, other: Any?) {
    val sameObject = first === twin
    val equalTwin = first == twin
    val equalOther = first == other

    val name: String = when {
        sameObject -> "singleton"
        !equalTwin -> "identity"
        !equalOther -> "content"
        else -> "partial-content"
    }

    fun report() {
        println("$label: same-object=$sameObject, twin=$equalTwin, other=$equalOther -> $name")
    }
}

fun summarize(checks: List<Criterion>) {
    for (name in listOf("singleton", "identity", "content", "partial-content")) {
        println("$name: ${checks.count { it.name == name }}")
    }
}
```

A function type is an ordinary type: it can be written as the type of a variable, a parameter, or a return value. In the line `val increment: (Int) -> Int = { it + 1 }` we wrote the type explicitly, so there is no need to declare a parameter in the lambda, and `it` can be used. When the type is not written, the parameter is declared inside the lambda in the form `n: Int ->`; the compiler has to learn the parameter type from one of the two ways.

The program below shows these together. `applyTwice` applies an operation two times, and `makeAdder` returns a function.

```kotlin
// Main.kt — lambda, it, trailing lambda, and function type
fun applyTwice(value: Int, operation: (Int) -> Int): Int = operation(operation(value))

fun makeAdder(amount: Int): (Int) -> Int = { it + amount }

fun main() {
    val twice = { n: Int -> n * 2 }
    val increment: (Int) -> Int = { it + 1 }
    println(twice(4))
    println(applyTwice(3, increment))
    println(applyTwice(3) { it * 10 })
    val addFive = makeAdder(5)
    println(addFive(10))
    println(listOf(1, 2, 3).map { it * it })
}
```

```
8
5
300
15
[1, 4, 9]
```

`twice(4)` called a lambda directly and gave `8`. `applyTwice(3, increment)` added one to three twice, and `5` came out. `applyTwice(3) { it * 10 }` is the trailing lambda syntax: three, thirty, three hundred; the result is `300`. `makeAdder(5)` returned a function that adds five, and this function gave `15` on `10`. In the last line `map` applied the lambda to each element, and `[1, 4, 9]` came out.

**LB45.** `it` was shown only in lambdas with a single parameter; the naming of parameters in a lambda with several parameters stayed limited to the single parameter in the `twice` example.

**LB46.** Higher-order functions were shown only on `Int` and with `map`; the other collection functions such as `filter` and `fold` are the work of a later topic.

#### Are Two Lambdas Equal

The measurement is built from four observations. `two-lambdas`: the expression `{ 1 }` written in two separate places. `same-call-site`: the `makeConstant` function, which returns the lambda `{ 1 }` written in one place, called twice. `capturing-lambda`: a lambda that uses an outer variable, that is, the `amount` parameter, with `makeAdderFor(1)` called twice. `function-reference`: `::inc` twice, one more repetition of the observation of the previous lesson.

Capture is when a lambda uses a variable outside its own body. The lambda inside `makeAdderFor` captures the value of `amount`; `{ 1 }`, on the other hand, captures nothing from outside.

```kotlin
// Main.kt — lambdas and identity
fun makeConstant(): () -> Int = { 1 }

fun makeAdderFor(amount: Int): (Int) -> Int = { it + amount }

fun inc(n: Int): Int = n + 1

fun dec(n: Int): Int = n - 1

fun main() {
    val checks = listOf(
        Criterion("two-lambdas", { 1 }, { 1 }, { 2 }),
        Criterion("same-call-site", makeConstant(), makeConstant(), { 2 }),
        Criterion("capturing-lambda", makeAdderFor(1), makeAdderFor(1), makeAdderFor(2)),
        Criterion("function-reference", ::inc, ::inc, ::dec),
    )
    for (check in checks) {
        check.report()
    }
    summarize(checks)
}
```

```
two-lambdas: same-object=false, twin=false, other=false -> identity
same-call-site: same-object=true, twin=true, other=false -> singleton
capturing-lambda: same-object=false, twin=false, other=false -> identity
function-reference: same-object=false, twin=true, other=false -> content
singleton: 1
identity: 2
content: 1
partial-content: 0
```

`two-lambdas` is identity: two lambdas with the same body that are written separately are not equal, `twin=false`. `capturing-lambda` is also identity: when `makeAdderFor(1)` was called twice, two separate lambda objects were born, and even though they captured the same value, they are not equal.

`same-call-site` is a singleton: `makeConstant()` returned the same object in two calls, `same-object=true`. This lambda captures nothing from outside; in the sandbox it was observed as the same instance at every call. `function-reference` is content, and the result of the previous lesson was verified again here. The summary lines give one singleton, two identity, one content, and zero partial content.

**LB47.** That a lambda that captures nothing is a single object was measured on the JVM target of Kotlin 2.4.21; this is not a language contract but a result observed in that version.

**LB48.** The `capturing-lambda` observation was made with the capture of a single `Int`; it is not claimed that the result stays the same when the type or the number of captured variables changes.

#### The Captured Variable Is Separate for Each Creation

A lambda can read and change a `var` variable outside itself. The question is how many copies of this variable exist. The block below writes a function that produces a counter, sets up two separate counters, and gives the same counter two separate names.

```kotlin
// Main.kt — a captured var is separate for each creation
fun makeCounter(): () -> Int {
    var count = 0
    return { ++count }
}

fun main() {
    val first = makeCounter()
    val second = makeCounter()
    println(first())
    println(first())
    println(second())
    println(first())
    val shared = makeCounter()
    val alias = shared
    println(shared())
    println(alias())
}
```

```
1
2
1
3
1
2
```

`first` was called twice and gave `1` and `2`; `second` gave `1` at its first call. The two counters captured separate `count` variables, because a new `count` is created each time `makeCounter` is called. When `first` was called again, it printed `3`, and the call of `second` did not affect it. The last two lines gave `1` and `2`: since `alias` points to the same lambda object as `shared`, both incremented the same counter.

The rule is this: the captured variable lives together with the lambda object, and there is one copy per object. The frequent mistake is to assume that the counter is reset each time the function is called, or to assume that all lambdas share the same variable. In Java, a lambda can capture only variables that are effectively final, while Kotlin allows capturing a `var`; behind this, the variable is known to be wrapped in a reference box, but this was not measured in this lesson.

**LB49.** The counter measurement was made on a single thread; what happens when several threads increment the same captured variable was not measured in this lesson.

#### Returning from a Lambda

Writing `return` inside a lambda sometimes ends the function and sometimes gives an error. The block below tries three cases: an unlabeled `return` inside `forEach`, `return@forEach` inside `forEach`, and `return@label` with its own label inside `map`.

```kotlin
// Main.kt — returning from a lambda
fun findFirstEven(values: List<Int>): Int {
    values.forEach {
        if (it % 2 == 0) return it
    }
    return -1
}

fun main() {
    println(findFirstEven(listOf(1, 3, 4, 6)))
    println(findFirstEven(listOf(1, 3)))
    val visited = mutableListOf<Int>()
    listOf(1, 2, 3, 4).forEach {
        if (it == 2) return@forEach
        visited.add(it)
    }
    println(visited)
    val labeled = listOf(1, 2, 3).map label@{
        if (it == 2) return@label 20
        it
    }
    println(labeled)
}
```

```
4
-1
[1, 3, 4]
[1, 20, 3]
```

`findFirstEven(listOf(1, 3, 4, 6))` gave `4`: `forEach` is an inline function, so the `return` inside the lambda ends the outer function, and 4, the first even number, is returned. In the second call there was no even number, so the loop ended and `-1` was returned.

The third line gave `[1, 3, 4]`. `return@forEach` ends only that run of the lambda, that is, it behaves like `continue`: 2 was skipped, and the rest were added. The fourth line gave `[1, 20, 3]`; `return@label` sets the value of the lambda, that is, inside `map` the value 2 was replaced with 20.

The frequent mistake is to expect an unlabeled `return` inside `forEach` to skip only one element: it ends the whole function. This behavior is possible because `forEach` is inline; in a lambda that is not inline, an unlabeled `return` gives a compile error, but this branch was not measured. In Java, a `return` inside a lambda exits only the lambda and does not end the outer method.

**LB50.** The return behavior was measured with `forEach` and `map`; the error message given when an unlabeled `return` is written in a higher-order function that is not inline was not measured in this lesson.

#### Lambda with Receiver and SAM Conversion

Some lambdas reach the members of a receiver object directly. The block below sets up a lambda of type `StringBuilder.() -> Unit`, writes lambdas for the Java interfaces `Runnable` and `Comparator`, and produces a lambda that combines two functions.

```kotlin
// Main.kt — lambda with receiver and SAM conversion
fun main() {
    val build: StringBuilder.() -> Unit = {
        append("kot")
        append("lin")
    }
    println(StringBuilder().apply(build).toString())
    val task = Runnable { println("ran") }
    task.run()
    val byLength = Comparator<String> { a, b -> a.length - b.length }
    println(listOf("ccc", "a", "bb").sortedWith(byLength))
    val compose: ((Int) -> Int, (Int) -> Int) -> (Int) -> Int = { f, g -> { x -> g(f(x)) } }
    val doubleThenInc = compose({ it * 2 }, { it + 1 })
    println(doubleThenInc(5))
}
```

```
kotlin
ran
[a, bb, ccc]
11
```

The first line gave `kotlin`. The `build` variable is a lambda with a receiver: `append` is called directly in its body, because a `StringBuilder` is given as `this`. `apply(build)` ran it on a new `StringBuilder`. The second line is `ran`: the `Runnable { ... }` syntax creates a Java interface that has a single abstract method with a lambda. This conversion is called SAM conversion (single abstract method). The third line gave `[a, bb, ccc]`: `Comparator` was also written with a lambda in the same way and sorted by length.

The fourth line gave `11`. `compose` takes two functions and returns a third; `doubleThenInc(5)` first multiplies 5 by two (10), then adds one (11). The order matters: since it is written `g(f(x))`, `f` runs first. The frequent mistake is to assume the reverse order in a composition.

**LB51.** SAM conversion was measured with Java interfaces; that an interface written in Kotlin needs a separate `fun interface` declaration was not measured in this lesson.

#### Habits That Come from the Measurements

Three questions should be asked when working with lambdas: what is captured, where does the code return from, and which type is expected. On capture, the measurement gave a clear rule: the captured variable lives together with the lambda object, and a separate copy is created at each creation. This makes it possible to build small objects that carry state, such as a counter or a cache, without writing a class; but because where the state is stored is not visible in the code, it is a hidden dependency for the reader. If the state needs to be long-lived or shared, an explicit class is more readable.

On returning, the most important distinction is whether the lambda is inline. An unlabeled `return` inside `forEach` ends the outer function; in the measurement `findFirstEven` found the first even number and returned. This behavior is useful, because it expresses an early exit without writing a loop. But if it is used wrongly where `continue` is expected, it quietly ends the whole function. To skip, `return@forEach` is written; to set the value, `return@label` is written. For readability, lambdas with more than one return point should be split into small named functions.

On types, a lambda with a receiver (`StringBuilder.() -> Unit`) works with `this` in the body and reaches the members of the object directly. This is the basis for writing builder code, and it is the mechanism of scope functions in the next lesson. SAM conversion makes it possible to build Java interfaces with a lambda; for interfaces written in Kotlin, `fun interface` is needed, and this was not measured.

The `compose` example, which combines functions, shows that functions are values. Since the order is `g(f(x))`, `f` runs first, and for the input 5 the result was 11. The reverse order would give a different result, but that calculation was not measured; only the output 11 came from the sandbox. For this reason, testing the order of a composition with a small example is enough. In function composition libraries this order is often confused, so it needs to be verified with a small example.

Comparison with Java: a Java lambda captures only variables that are not reassigned, and `return` exits only the lambda. Kotlin offers a wider behavior in both; the cost of the flexibility is that the reader must know which `return` ends what. When reviewing code, looking first at whether the enclosing function is inline and then at whether there is a label, for each `return`, removes this ambiguity quickly.

#### The Bounding Measurement

The identity criterion for lambdas is broken by two limits. The first is capture: a lambda that captures nothing becomes a single object at the call site and comes out as a singleton, while a lambda that captures becomes a new object at each call and comes out as identity. That is, whether two lambdas are equal is determined not by their bodies but by the place where they are written and whether they capture something. The second is the function reference: when `::inc` is taken twice, the objects are separate but equal.

#### Summary

- A lambda is written in the form `{ parameters -> body }`; a single parameter can take the name `it`, and if the last parameter has a function type, the lambda can be written outside the parentheses.
- A function type has the form `(Int) -> Int`; a function that takes or returns a function is called a higher-order function.
- Two lambdas with the same body are not equal if they are written separately: identity. A capturing lambda is a new object at every call.
- A lambda that captures nothing gave the same object every time from the same call site: singleton. `::inc` taken twice is content.
- This lesson's criterion: **identity**; bounding measurement: a lambda that captures nothing is a singleton, and the `::inc` reference is content.

#### Next Step

We have seen how to carry lambdas as values. In the last lesson we examine five ready-made higher-order functions that run a lambda in the context of an object: scope functions. `let`, `run`, `with`, `apply`, and `also` work on the same object, but one gives the object as `this`, another as `it`; we also measure which of them returns what.

### Scope Functions

Source: https://academia.sh/en/courses/kotlin/scope-functions

Introduces the scope functions `let`, `run`, `with`, `apply`, and `also`; measures whether the receiver is `this` or `it` and what each returns, and shows that the result does not change the equality criterion.

In the previous lesson we carried lambdas as values. Scope functions are five ready-made higher-order functions that run these lambdas in the context of an object: `let`, `run`, `with`, `apply`, and `also`. The difference between them comes from two questions: how is the object reached inside the lambda, and what does the function return? At the end of the lesson a third question arrives: does the equality criterion of an object change when it passes through a scope function?

#### Receiver and Return Value

A scope function gives the object it is called on to the lambda in one of two ways. `let` and `also` give the object as the lambda parameter `it`. `run`, `with`, and `apply` make the object the receiver: inside the lambda, the members of the object are reached with `this`, and most often with no prefix at all. This distinction is the first place to look when reading which object an expression works on.

The return value has two forms. `let`, `run`, and `with` return the result of the last expression of the lambda. `apply` and `also` return the object itself. That is, `apply` is used while building an object, and `also` is used to do a side job without touching the object.

In this lesson too, the measurements are made with the helper file introduced in the first lesson. The `Criterion` class takes a value, its twin, and a different value, and asks three questions; the file is given below exactly as it was.

```kotlin
// Criterion.kt — classifies the criterion that makes two values equal
class Criterion(val label: String, first: Any?, twin: Any?, other: Any?) {
    val sameObject = first === twin
    val equalTwin = first == twin
    val equalOther = first == other

    val name: String = when {
        sameObject -> "singleton"
        !equalTwin -> "identity"
        !equalOther -> "content"
        else -> "partial-content"
    }

    fun report() {
        println("$label: same-object=$sameObject, twin=$equalTwin, other=$equalOther -> $name")
    }
}

fun summarize(checks: List<Criterion>) {
    for (name in listOf("singleton", "identity", "content", "partial-content")) {
        println("$name: ${checks.count { it.name == name }}")
    }
}
```

This choice is a readability decision. If you want to use the members of the object inside the lambda without a prefix, one of the three functions with the receiver form is suitable; if you will pass the object under a name as an argument to something else, the two with the `it` form are suitable. The return value decides what you are left with after the block: the object itself, or a computed result.

The program below runs the five functions in turn. `Point` is a data class with two mutable fields.

```kotlin
// Main.kt — let, run, with, apply, and also
data class Point(var x: Int, var y: Int)

fun main() {
    val letResult = Point(1, 2).let { it.x + it.y }
    val runResult = Point(3, 4).run { x * y }
    val withResult = with(Point(5, 6)) { "$x,$y" }
    val applied = Point(0, 0).apply {
        x = 7
        y = 8
    }
    val alsoResult = Point(9, 9).also { println("observed: ${it.x}") }
    println(letResult)
    println(runResult)
    println(withResult)
    println(applied)
    println(alsoResult)
}
```

```
observed: 9
3
12
5,6
Point(x=7, y=8)
Point(x=9, y=9)
```

The first line came from the `println` call inside `also` and wrote `observed: 9`; the `also` block observed the object, and the object was returned as it was. In the `let` block the two fields were added through `it`, and `3` came out. In the `run` block `x * y` was written with the receiver, without a prefix, and `12` came out. `with` used the same receiver syntax and gave the text `"5,6"`, which is the result of the lambda. In the `apply` block the fields were assigned, and the function returned the object itself: `Point(x=7, y=8)`. The last line shows the object returned by `also`: `Point(x=9, y=9)`.

**LB52.** The five functions were measured only on `Point` and only with integer fields; uses of `?.let`, `?.apply`, and `takeIf` on a receiver that can be null are measured later in this lesson, only with `String?`.

**LB53.** `with` is not an extension but an ordinary function that takes the object as an argument; this distinction was only stated here, not measured.

#### A Scope Function Does Not Change the Criterion

The measurement asks this: does passing an object through a scope function change whether its twin is counted as equal? Here `Point` is a data class with fields that do not change. We write three observations. `let-copy`: `Point(1, 2).let { it.copy() }`. `run-copy`: `Point(1, 2).run { copy() }`; the same copy, this time with the receiver syntax. `also-twin`: the same `source` variable is passed through `also { }` and compared with itself.

Since `let` and `run` return the result of the lambda, what is returned is a separate object built with `copy()`. `also`, on the other hand, returns its own object.

```kotlin
// Main.kt — what a scope function returns
data class Point(val x: Int, val y: Int)

fun main() {
    val source = Point(1, 2)
    val checks = listOf(
        Criterion("let-copy", Point(1, 2), Point(1, 2).let { it.copy() }, Point(1, 3)),
        Criterion("run-copy", Point(1, 2), Point(1, 2).run { copy() }, Point(1, 3)),
        Criterion("also-twin", source, source.also { }, Point(1, 3)),
    )
    for (check in checks) {
        check.report()
    }
    summarize(checks)
}
```

```
let-copy: same-object=false, twin=true, other=false -> content
run-copy: same-object=false, twin=true, other=false -> content
also-twin: same-object=true, twin=true, other=false -> singleton
singleton: 1
identity: 0
content: 2
partial-content: 0
```

`let-copy` and `run-copy` are content: `same-object=false`, `twin=true`. The copy that comes out of the scope function is not the same object as the source, but since it carries the same values, the `equals` of the data class counts them as equal. Which scope function was used did not determine the criterion; what determines the criterion is that `Point` is a data class.

`also-twin` is a singleton: `same-object=true`. Since `also` returns the object itself, the twin and the first value came out as the same object. The summary lines give one singleton, zero identity, two content, and zero partial content.

**LB54.** In the `also-twin` observation no twin was built; since the scope function returns the object itself, the comparison was made between an object and itself. This observation breaks the measurement condition, as explained below.

**LB55.** The `copy()` call produces a shallow copy; how the copy behaves in a data class that carries a nested object was not measured in this lesson.

#### What the Five Functions Return

The real difference between scope functions comes down to two things: is the object reached with `it` or with `this`, and what does the expression return? The block below runs all five on the same `Point` object and tests the returned object against the source with `===`.

```kotlin
// Main.kt — what the scope functions return
data class Point(var x: Int, var y: Int)

fun main() {
    val p = Point(1, 2)
    println(p.let { it } === p)
    println(p.run { this } === p)
    println(with(p) { this } === p)
    println(p.apply { x = 10 } === p)
    println(p.also { it.y = 20 } === p)
    println(p.let { 5 })
    println(p.apply { x = 11 }.run { x + y })
    println(p)
}
```

```
true
true
true
true
true
5
31
Point(x=11, y=20)
```

All five lines gave `true`, but not for the same reason. `p.let { it } === p` and `p.run { this } === p` are true because the last expression of the lambda is the object itself. `with(p) { this } === p` is true for the same reason. `apply` and `also`, on the other hand, do not look at the value of the lambda; they always return the receiver object.

The sixth line gave `5`: the value of the expression `let { 5 }` is the last expression of the lambda, not the object. The seventh line is `31`: `apply { x = 11 }` returned the object, and `run { x + y }` computed the sum, the sum of 11 and 20, which is 31. The last line gave `Point(x=11, y=20)`, that is, the assignments in the previous five calls changed the object itself. These assignments were not made on a local copy but on `p`.

The rule: `let`, `run`, and `with` give the result of the lambda; `apply` and `also` give the object. The frequent mistake is to write a value on the last line inside `apply` and assume that it will be returned. Java has no equivalent construct; for chaining, the methods have to return `this` (the builder pattern).

**LB56.** The return values were measured with `Point` and integer results; `with` is not an extension but an ordinary function, and this distinction was not measured.

#### Scope Functions with null, and takeIf

The most frequent use is to process a value that may be null safely with `?.let`. The block below tries this with a filled and an empty `String?`, looks at the difference between `?.let` and `.let { it?.... }`, and tests the functions `takeIf` and `takeUnless`.

```kotlin
// Main.kt — scope functions with null, and takeIf
fun main() {
    val present: String? = "kotlin"
    val missing: String? = null
    println(present?.let { it.length })
    println(missing?.let { it.length })
    println(missing.let { it?.length })
    println(missing?.apply { length })
    println(missing?.let { it.length } ?: -1)
    println(present.takeIf { it?.startsWith("k") == true })
    println(present.takeUnless { it?.startsWith("k") == true })
    println("abc".takeIf { it.length > 5 })
}
```

```
6
null
null
null
-1
kotlin
null
null
```

`present?.let { it.length }` gave `6`; `missing?.let { it.length }` gave `null`. The safe call `?.` did not run the lambda at all when the receiver was null. The third line also gave `null`, but the reason is different: in the expression `missing.let { it?.length }`, `let` is also called on the null value, the lambda runs, and `it?.length` inside produces a null result. Even if the results of these two forms are the same, the lambda did not run in the first and did run in the second. The fourth line is `null`: `missing?.apply { length }` also ran nothing because the receiver was null, and the result is the null value. The fifth line is `-1`: the elvis operator turned the null result into a default.

`takeIf` returns the object if the condition is true and `null` if it is false. `present.takeIf { ... }` gave `kotlin`, because the string starts with `k`. `takeUnless` works in reverse and gave `null` with the same condition. `"abc".takeIf { it.length > 5 }` gave `null`, because the condition was not met. The frequent mistake is to use the result of `takeIf` directly, forgetting that it is of a nullable type; the compiler reports a compile error in this case, but this error was not measured.

**LB57.** The null measurement was made with `String?`; side effects in the `takeIf` condition and chains of `takeIf` were not measured in this lesson.

#### Nested Scope Functions and this

When two scope functions are written nested, which object do `this` and `it` look at? The block below runs a second `run` inside a `run`, reaches the outer receiver with a label, then uses a `run` without an object, and reads `size` in nested `apply` calls.

```kotlin
// Main.kt — nested scope functions and this
data class Box(var size: Int)

fun main() {
    val box = Box(1)
    val result = box.run outer@{
        size = 5
        Box(size * 2).run { "inner: $size, outer: ${this@outer.size}" }
    }
    println(result)
    val note = run {
        val temp = 21
        temp * 2
    }
    println(note)
    Box(3).apply {
        Box(4).apply {
            println(size)
        }
        println(size)
    }
}
```

```
inner: 10, outer: 5
42
4
3
```

The first line gave `inner: 10, outer: 5`. In the outer `run`, `size = 5` was assigned, and in the inner `run` a new `Box(10)` was built with `size * 2`. In the inner block, `size` looks at the nearest receiver, that is, the `Box(10)` object. To reach the outer receiver, `this@outer.size` is written, and `5` is read. For this reason, nested receiver use produces code that is hard to read; giving the name `it` with `also` and `let` is clearer. The frequent mistake is to write `size` in the inner block and assume that the field of the outer object is being read.

The second line gave `42`: a `run { }` without an object only opens a block and returns the last expression. The third and fourth lines gave `4` and `3`: inside the inner `apply`, `size` is the value 4 of the inner object, and inside the outer `apply` it is the value 3 of the outer object. When the inner block ends, the scope returns to the outer one. In Java, the same effect in inner classes comes from the syntax `Outer.this.field`; in Kotlin, the label is its counterpart.

**LB58.** The nested receiver measurement was made with two levels; the resolution of an unlabeled `this` in use nested three or more levels deep was not measured in this lesson.

#### Habits That Come from the Measurements

Two questions are asked when choosing a scope function: is the object reached with `it` or `this`, and will the result be the object or the value of the lambda? The measurement confirmed the table: `apply` and `also` return the object, and `let`, `run`, and `with` return the value of the lambda. In practice, `apply` is used while building an object, `also` when adding a side effect (such as writing a log), `let` when transforming a value that may be null, and `run` and `with` when doing a calculation on an object and taking the result. This pairing is not a rule but a common habit; the same job can often be done with another function as well.

Overuse is a real risk. Three nested scope functions make it hard to follow which object the names `this` and `it` go to. In the measurement, `size` inside the inner `run` went to the inner object, and access to the outer value was made with `this@outer.size`. In use that goes beyond two nested levels, named local variables are clearer. As a rule, if one scope function is written inside another, either the outer one should be named with `it` or the code should be moved to a separate function.

With null values, `?.let` is the common pattern, and the measurement showed the difference between the two forms: `missing?.let { ... }` does not run the lambda at all, while `missing.let { it?.... }` runs the lambda with the null value. Even if the results look the same, the difference appears in a lambda that carries a side effect. `takeIf` and `takeUnless` put a condition into the chain: if the condition is not met, `null` returns and the following `?.` steps are skipped.

Another frequent mistake is to write a value on the last line of an `apply` block and assume that it will be returned. `apply` always returns the object, so the expression at the end is ignored. If you want to turn the result into another value, `let` or `run` should be chosen. In Java, the counterpart of these jobs is the builder pattern, which depends on every method returning `this`; Kotlin provides this at the language level, without changing the existing class.

Finally, `with` is not an extension but an ordinary function that takes the object as an argument. For this reason it cannot be attached with `?.` to an object that may be null; for a nullable receiver, `?.run` or `?.let` should be chosen.

#### The Bounding Measurement

The `also-twin` observation breaks the measurement condition: `Criterion` expects a separately built twin of a value, whereas `also` gives the same object back. The singleton result that came out is not a finding about the criterion but a reflection of the return value rule of the function. For this reason, to show that a scope function does not change the criterion, the `let` and `run` observations are meaningful, while the `also` observation shows only the limit. If you see a scope function in an expression, ask these two questions: which object is this expression working on, and does the function return this object or the result of the lambda? The answer to the equality question always stays in the own type of the object.

#### Summary

- `let` and `also` give the object as `it`; `run`, `with`, and `apply` give it as the receiver (`this`).
- `let`, `run`, and `with` return the result of the lambda; `apply` and `also` return the object itself.
- `Point(1, 2).let { it.copy() }` and `run { copy() }` gave separate but equal objects: content.
- Since `also` gives the same object back, `also-twin` came out as a singleton and broke the measurement condition.
- This lesson's criterion: **content**; bounding measurement: the `also` twin is a singleton and breaks the measurement condition because it builds no twin.

#### Next Step

The language basics close here. In the next topic, `null` enters the equality question: when a value can be absent, is `null` the same as `null`, and how does the compiler follow this case inside the type? The topic starts with the lesson Null Safety.

## Types and Null Safety

Nullable and non-nullable types, safe calls and the Elvis operator, primary and secondary constructors, data classes, sealed types and enumerations, object declarations, interfaces and delegation, extension functions, and generics and variance.

### Null Safety

Source: https://academia.sh/en/courses/kotlin/null-safety

A measurement of how nullable and non-nullable types are separate types, how a type narrows after a null check, and which criterion null equal to null produces.

The previous lesson measured which value a scope function returns. Sometimes what comes back is nothing at all: a search finds no result, a field has not been filled in, a conversion cannot finish. That situation can be expressed with a value such as `null`; the value then has to be checked before every use, and when the check is forgotten the error surfaces at run time.

Kotlin writes absence into the type itself. A `String` is always a string; a `String?` is either a string or `null`. These two types are not the same type, and the compiler does not let you use one in place of the other. This is called **null safety**: a nullable type and a non-nullable type are separate types, so every place where `null` is possible is visible in the source code. This lesson measures that distinction through the rejection the compiler issues, then looks at what the same distinction says about the equality question.

The course's measurement axis is the relationship between a value and another value that counts as equal to it. A **criterion** takes one of four forms: **singleton**, **identity**, **content**, and **partial content**. The rule works in order. If the first value and its twin are the same object, the criterion is singleton. If not, and the twin is not equal, it is identity: only the same object counts as equal. If the twin is equal but a third value that differs from the first in one respect is not equal, it is content: the objects are separate and the equality comes from the content. If the third value is also equal, it is partial content: the respect in which it differs does not affect equality. In the binding measurement of the opening lesson the distribution over eight values was 2/2/3/1; this lesson measures only its own slice.

The `Criterion` class that does the classification is given below as it is. Code blocks are compiled and run only inside the sandbox; every line in the output blocks is what the sandbox printed.

```kotlin
// Criterion.kt — classifies the criterion that makes two values equal
class Criterion(val label: String, first: Any?, twin: Any?, other: Any?) {
    val sameObject = first === twin
    val equalTwin = first == twin
    val equalOther = first == other

    val name: String = when {
        sameObject -> "singleton"
        !equalTwin -> "identity"
        !equalOther -> "content"
        else -> "partial-content"
    }

    fun report() {
        println("$label: same-object=$sameObject, twin=$equalTwin, other=$equalOther -> $name")
    }
}

fun summarize(checks: List<Criterion>) {
    for (name in listOf("singleton", "identity", "content", "partial-content")) {
        println("$name: ${checks.count { it.name == name }}")
    }
}
```

The parameters have type `Any?`, which means `null` can be an argument too. This lesson's first question is exactly that: when `null` is treated as a value, which criterion does it produce?

#### Two Separate Types

In the block below, a `String` and a `String?` with the same content stand side by side. The comparison compiles, because `==` does not require the two sides to have matching types; it looks only at the content. But an operation that depends on the type, such as taking the length, is trouble-free only on the non-nullable type.

```kotlin
// Main.kt — two types
fun main() {
    val a: String = "cube"
    val b: String? = "cube"
    println(a == b)
    println(a.length)
    val c: String? = null
    println(c == null)
    println(c)
}
```

```
true
4
true
null
```

`a == b` is true: both strings carry the same content, and the filled-in state of the `String?` type is compared as a string. `c` carries `null`, and `null` is written to the screen; printing a nullable variable is not an error. The point to notice is that in the `a.length` line `a` is a non-nullable type. Writing the same line with `b` would lead to a compiler rejection.

There are two rejections, and both were measured in the sandbox. The first is assigning `null` to a variable of type `String`. In Kotlin 2.4.21 the compiler rejects this: `null` cannot be a value of a non-nullable type. The error appears at compile time, and the program never runs.

```kotlin
fun main() {
    val text: String = null
    println(text)
}
```

The second is asking for `length` directly on a variable of type `String?`. The compiler rejects this as well: when the receiver is a nullable type, only a safe call or a not-null assertion is allowed. The safe call is the subject of the next lesson; the not-null assertion is also measured there, together with its failure.

```kotlin
fun main() {
    val text: String? = "cube"
    println(text.length)
}
```

**TN1.** These two rejections were measured only for a local variable; they were not tried separately for a class property, a function parameter, or a return type. The error text depends on the compiler version and is not reproduced here; what was measured is that the program never runs.

#### The Type Narrows After a Null Check

To get the length of a `String?` value, you first have to make sure it is not `null`. Kotlin does not make you write a separate conversion for this. When a `null` check is made inside an `if`, the compiler narrows the variable's type to `String` within that branch. This behavior is called a **smart cast**: the programmer writes no conversion, and the check itself changes the type.

In the block below, the `lengthOf` function takes a `String?`; inside the `if (text != null)` branch, `text.length` is called directly. The block also filters a list containing two `null` values with `filterNotNull`; the result is a `List<String>` rather than a `List<String?>`, and its elements give `length` without any conversion.

```kotlin
// Main.kt — smart cast
fun lengthOf(text: String?): Int {
    if (text != null) {
        return text.length
    }
    return -1
}

fun main() {
    println(lengthOf("cube"))
    println(lengthOf(null))
    val values = listOf("a", null, "bb", null)
    println(values.size)
    val present = values.filterNotNull()
    println(present.size)
    println(present.sumOf { it.length })
}
```

```
4
-1
4
2
3
```

`lengthOf("cube")` gives four, and `lengthOf(null)` gives `-1`. The list has four elements, but `filterNotNull` discards the two `null` values and leaves two strings; the sum of their lengths is three. What is measured here is that a `null` check is not merely a condition: after the check, the same variable is a different type.

**TN2.** The smart cast was measured only through `if` and `filterNotNull`; it was not tried for a check inside `when`, for checks chained with `&&`, or for when the narrowing breaks with mutable variables. The narrowing holds as long as the variable is known not to change between the check and the use.

#### Where the Question Mark Goes

The `?` mark can be written in only one place of a type, and where it goes changes the meaning. `List<String?>` is a list whose elements can be empty: the list exists, and some places inside it are `null`. `List<String>?` is a type whose list itself can be empty: the list either exists or it does not, but if it exists, none of its elements is `null`. The two are independent, and both can be written at the same time.

The block below puts the two cases side by side. The size of the list with nullable elements is read directly, because the list itself cannot be null. With the type whose list itself is nullable, a `null` check is needed first; after the check, the same variable behaves as a `List<String>` and `size` is called directly.

```kotlin
// Main.kt — where the question mark goes
fun main() {
    val items: List<String?> = listOf("a", null)
    val maybeList: List<String>? = null
    println(items.size)
    println(maybeList == null)
    if (maybeList != null) {
        println(maybeList.size)
    } else {
        println("no-list")
    }
}
```

```
2
true
no-list
```

**TN3.** This measurement deals not with an empty list but with a list that is absent altogether (`null`); the behavior of an empty but existing list was not tried. The `List<String?>?` form, in which both `?` marks are written together, was not measured either.

#### null with null

On the course's measurement axis, the real question is this: are two `null` values the same object? In Kotlin `null` is not an instance of a class but a single value. Every `null` is the same as every other; there is no identity that could tell one from another. As a result, referential equality (`===`) and structural equality (`==`) give the same answer for two `null` values. The rule stops at the first question: if the first value and its twin are the same object, the criterion is singleton and the remaining questions are not consulted.

The block below has four observations. In the first two, the first value and its twin are `null`, and the third value is filled in on one and `null` again on the other. The third and fourth observations use filled-in strings: the two strings are built separately with `buildString`, so two separate objects carrying the same content are obtained.

```kotlin
// Main.kt — null with null
fun main() {
    val nullFirst: String? = null
    val nullTwin: String? = null
    val full1 = buildString { append("cu"); append("be") }
    val full2 = buildString { append("cu"); append("be") }
    val checks = listOf(
        Criterion("null-null", nullFirst, nullTwin, "cube"),
        Criterion("null-null-null", nullFirst, nullTwin, null),
        Criterion("full-full", full1, full2, "another"),
        Criterion("full-null", full1, nullTwin, "another"),
    )
    for (check in checks) check.report()
    summarize(checks)
}
```

```
null-null: same-object=true, twin=true, other=false -> singleton
null-null-null: same-object=true, twin=true, other=true -> singleton
full-full: same-object=false, twin=true, other=false -> content
full-null: same-object=false, twin=false, other=false -> identity
singleton: 2
identity: 1
content: 1
partial-content: 0
```

The result reads off four lines. `null` with `null` is singleton. Two filled-in strings, with the same content but as separate objects, give the content criterion: `same-object=false`, `twin=true`, `other=false`. A filled-in string against `null` is identity: the twin is not equal. The criterion for the filled-in strings is the result of `String`'s own `equals`, not of any type redefined in this lesson.

There is a trap here: because the criterion for two `null` values came out singleton, you cannot conclude that everything compared with `null` is singleton. Only `null` with `null` is singleton. A filled-in value against `null` is identity, because the twin is not equal.

**TN4.** This measurement covers only `null` values of type `String?`; whether two `null` values of another type count as the same object was not tried. That the filled-in strings are separate objects was read from the `===` result when they were built with `buildString`; whether constant strings are shared as the same object was not tried.

#### Ready-Made Functions on a Nullable Value

A `null` check is not always written with an `if`. The standard library offers a few ready-made functions whose receiver is `String?`: `toString`, `isNullOrEmpty`, and `orEmpty`. These functions do not throw an exception on a `null` receiver, because their definitions account from the start for the receiver possibly being `null`. The block below tries these functions with three values (`null`, an empty string, and a filled-in string). It also looks at what comes out when a string is concatenated with `null`.

```kotlin
// Main.kt — ready-made functions on a nullable receiver
fun main() {
    val missing: String? = null
    val empty: String? = ""
    val present: String? = "ab"
    println(missing.toString())
    println("[" + missing + "]")
    println(missing.isNullOrEmpty())
    println(empty.isNullOrEmpty())
    println(present.isNullOrEmpty())
    println(missing == null)
    println(missing.orEmpty().length)
}
```

```
null
[null]
true
true
false
true
0
```

Three things can be read from the output. First, `missing.toString()` throws no exception; on a `null` receiver it gives the string `"null"`. The `null` in the first line is therefore a string, not a `null` value. Second, concatenating a string with `null` works too, and the result is `[null]`: `String.plus` takes an `Any?` as its argument, so the compiler raises no objection; the side that can be null is the argument on the right. Third, `isNullOrEmpty` returns `true` for both `null` and the empty string, and `false` for the filled-in string; `orEmpty` converts `null` into the empty string, so the length comes out as `0`.

What these functions have in common is that the receiver type is `String?`. An ordinary `String` method cannot be called directly on this type, because the receiver may be absent. A common mistake is confusing the result of `toString` with a `null` value. The type of the result is always `String`; measuring the absence of a value by searching for the string `"null"` is wrong. Absence is asked about with `== null`. When code writes a string to a log, the two cases looking the same is the main reason the error is noticed late.

#### Type Conversion and Null

Type conversion meets `null` as well. The `as` operator forces the conversion, `as?` gives `null` when it fails, and `is` only asks a question. The block tries all three on an `Any?` whose value is `null`; if the conversion fails, only the class name of the thrown exception is printed. In the last line, a filled-in value of the wrong type is converted with `as?`.

```kotlin
// Main.kt — as and as? on null
fun main() {
    val value: Any? = null
    println(value as? String)
    println(value is String)
    println(value is String?)
    try {
        val text = value as String
        println(text.length)
    } catch (e: Exception) {
        println(e::class.simpleName)
    }
    val other: Any? = 5
    println(other as? String)
}
```

```
null
false
true
NullPointerException
null
```

The three expressions show three different attitudes. When `as?` fails, it throws no exception and gives `null`; the result is `null` both for a `null` value and for a value of the wrong type. `is String` returns `false` for `null`, but `is String?` returns `true`, because the `null` value is a member of the `String?` type. The forcing `as String` throws a `NullPointerException` on a `null` value; the block catches it and prints only the class name.

The lesson here is that the two `null` outputs do not mean the same thing. The `null` in the first line comes from the value to be converted being absent in the first place. The `null` in the last line comes from the value being present but its type not fitting, and it is not possible to tell these two apart by looking at the output alone. When the distinction is needed, `value == null` is asked separately before the conversion. The forcing form of the conversion is written only where the type is known for certain; a wrong assumption comes back as an exception at run time, not at compile time.

#### The Bounding Measurement

This lesson's rule is: the criterion is the answer to the first question. The rule asks four questions in order and stops at the first "yes". The first two lines of the earlier section already showed this, but the difference has to be seen explicitly: in the `null-null` line the third value is filled in (`"cube"`), and in the `null-null-null` line the third value is `null`. The equality of the third value (`other`) came out opposite in the two lines, while the criterion is singleton in both. Nothing between `other=false` and `other=true` changes the criterion; this is because `Criterion` stops at the first question, and the third value adds no information to the criterion.

This shows where the limit lies. The `other` column is printed, because `Criterion` computes the third comparison in every case; but in a singleton criterion that column is not read. Saying that a value is singleton does not tell you how many values are equal to it. It says only that the twin is the same object.

The same limit can be read from the opposite direction. If the first value were `null` and its twin a filled-in string, `same-object=false` and `twin=false` would come out: the criterion would be identity. So starting with `null` does not make the criterion singleton; for it to be singleton, the twin must be `null` as well. What determines the criterion is not the type of the first value but whether the first value and its twin are the same object.

**TN5.** The bounding measurement was made with two `null` values and a filled-in or `null` third value; the ordering in which the first value is filled in and the twin is `null` was not given a separate output, and was only inferred from the identity result on the `full-null` line. `Criterion`'s rule was not tried with other orderings.

#### Summary

- `String` and `String?` are separate types: assigning `null` to a `String` and asking for `length` directly on a `String?` do not compile.
- A null check inside an `if` narrows the variable's type to `String` in that branch; this is a smart cast, and no conversion is written.
- `filterNotNull` turns a `List<String?>` into a `List<String>` and discards the `null` elements.
- Two `null` values are the same object and the criterion is singleton; two separate filled-in strings give content, and a filled-in string against `null` gives identity.
- The rule stops at the first question: in the first two lines the equality of the third value came out opposite while the criterion stayed the same.
- This lesson's criterion: **singleton**. `null` with `null` is the same object.

#### Next Step

In this lesson we saw `null` in the type and checked it with `if`. Every `null` check asks for a separate `if`, and the code grows longer as the checks line up one after another. The next lesson measures the operators that do the same job by a shorter route: where a `?.` safe call chain stops when it sees `null`, when the `?:` Elvis operator evaluates its right side, and which exception is thrown when a not-null assertion turns out wrong.

### Safe Calls and the Elvis Operator

Source: https://academia.sh/en/courses/kotlin/safe-calls-and-the-elvis-operator

A measurement of where a safe call chain stops when it meets null, when the Elvis operator evaluates its right side, and which exception a not-null assertion throws.

The previous lesson showed that a `null` check is written with an `if` and that the type narrows after the check. Doing the same job where several values are stacked on top of one another requires a separate `if` for every step. Kotlin shortens this with two operators: the safe call `?.` and the Elvis operator `?:`. There is a third route as well: taking the check away from the compiler and placing it on the programmer. This lesson measures all three and looks at where each one stops when it meets `null`.

The measurement axis is the same as in the previous lesson: the relationship between a value and another value that counts as equal to it, the **criterion**. The `Criterion` class is given again below, as it was in the opening lesson. The rule works in order: the same object gives singleton; otherwise an unequal twin gives identity; an equal twin with an unequal third value gives content; an equal third value too gives partial content. The previous lesson measured the singleton slice of the 2/2/3/1 distribution; this lesson looks at the content slice.

```kotlin
// Criterion.kt — classifies the criterion that makes two values equal
class Criterion(val label: String, first: Any?, twin: Any?, other: Any?) {
    val sameObject = first === twin
    val equalTwin = first == twin
    val equalOther = first == other

    val name: String = when {
        sameObject -> "singleton"
        !equalTwin -> "identity"
        !equalOther -> "content"
        else -> "partial-content"
    }

    fun report() {
        println("$label: same-object=$sameObject, twin=$equalTwin, other=$equalOther -> $name")
    }
}

fun summarize(checks: List<Criterion>) {
    for (name in listOf("singleton", "identity", "content", "partial-content")) {
        println("$name: ${checks.count { it.name == name }}")
    }
}
```

#### The Safe Call

A safe call checks whether the receiver is `null` before accessing a member. If the receiver is filled in, the call is made and the result returns; if the receiver is `null`, the call is never made and the value of the expression is `null`. The result type is therefore always a nullable type: `?.length` on a `String?` gives an `Int?`.

In chained safe calls, the first `null` value stops the chain. In an expression of the form `user.address?.city?.uppercase()`, if `address` is `null`, `city` is never looked at; if `city` is `null`, `uppercase` is not called. The block below measures this with three users: one fully filled in, one with no address at all, and one with an address but no city. It then asks a second question: is the argument of a method that is not called evaluated? The `trace` function prints a line every time it runs, so the output shows whether it ran. The last two lines ask the same question with `let`, whose block never runs on a `null` receiver.

```kotlin
// Main.kt — safe call chain
class Address(val city: String?)
class User(val name: String?, val address: Address?)

fun trace(label: String): String {
    println("evaluated: $label")
    return label
}

fun main() {
    val users = listOf(User("ada", Address("izmir")), User("can", null), User("eda", Address(null)))
    for (user in users) {
        println(user.address?.city?.uppercase())
    }
    val missing: String? = null
    println(missing?.plus(trace("argument")))
    val present: String? = "ab"
    println(present?.plus(trace("argument")))
    missing?.let { println("let ran") }
    present?.let { println("let ran: $it") }
}
```

```
IZMIR
null
null
null
evaluated: argument
abargument
let ran: ab
```

The first three lines give the behavior of the chain. For the fully filled-in user the result is `IZMIR`. For the user with no address, `null` is printed; the second link, the city, is never reached. For the user with an address but no city, `null` is printed as well; the third link, `uppercase`, is never called. All three results come from the same expression; only the place where the chain breaks changes.

The fourth line shows that when the receiver is `null`, the argument of the `plus` call is not evaluated: the `evaluated: argument` line does not appear after this call, only before the call whose receiver is filled in. An argument is not computed if the call will not be made. The same holds for `let`: on a `null` receiver the block does not run, and on a filled-in receiver it does, with `it` being the receiver itself.

**TN6.** This measurement covers only `uppercase`, `plus`, and `let`; the behavior of other methods and of a safe call in assignment form (writing a value to a property) was not tried. The argument not being evaluated was measured only with `plus`.

#### The Elvis Operator

`?:` produces a value: the left side itself if it is not `null`, and the right side if it is. The right side is evaluated only when the left side is `null`, just like the argument in a safe call. If the type of the left side is `String?`, the result of the operator is most often a non-nullable type; because if the right side gives a `String`, no path of the expression returns `null`.

The right side does not have to be a value. `return` and `throw` are expressions too, and their type counts as a subtype of all types; so `name ?: return "no-name"` and `name ?: throw ...` are valid. The first leaves the function early, the second throws an exception. If the left side is filled in, neither runs, and the following lines continue with a value of type `String`.

The block below measures four things. In the first two lines, does the right side's `trace` call run when the left side is filled in and when it is empty? In the next two lines, the `describe` function leaves early with `?: return`. In the last line, `needName` throws an `IllegalArgumentException` and the caller catches it; what is printed is only the exception's class name.

```kotlin
// Main.kt — Elvis
fun trace(label: String): String {
    println("evaluated: $label")
    return label
}

fun describe(name: String?): String {
    val clean = name ?: return "no-name"
    return "name=" + clean
}

fun needName(name: String?): String = name ?: throw IllegalArgumentException("empty")

fun main() {
    val present: String? = "ada"
    val missing: String? = null
    println(present ?: trace("right-side"))
    println(missing ?: trace("right-side"))
    println(describe(present))
    println(describe(missing))
    try {
        needName(missing)
    } catch (e: IllegalArgumentException) {
        println(e::class.simpleName)
    }
}
```

```
ada
evaluated: right-side
right-side
name=ada
no-name
IllegalArgumentException
```

When the left side is filled in, `trace` never runs: the first line prints only `ada`. When the left side is empty, `trace` runs, the `evaluated: right-side` line is printed, and `right-side`, the value of the expression, is written. `describe(present)` gives `name=ada` and `describe(missing)` gives `no-name`; in the second, the function exits with `return`, so the `"name=" + clean` line is never reached. The last line gives the class name of the thrown exception.

**TN7.** `return` and `throw` were measured only in a function body and a single-expression body; the use of `?: return` inside a lambda and the labeled `return` were not tried. The exception's message text was not printed; only the class name was measured.

#### The Not-Null Assertion

The third route is to take the check away from the compiler and give the responsibility to the programmer. The **not-null assertion** converts a `String?` value to `String` by claiming that it is not `null`. If the value really is filled in, there is no problem. If the value is `null`, the compiler does not report an error ahead of time; the error is thrown at run time as a `NullPointerException`. The lesson text refers to the operator by its name, while in the code block the mark is visible.

The block below makes two calls. On the filled-in value, the assertion passes and the length is printed. On the empty value, the assertion does not hold; the `try` block catches the exception and prints only the class name. The exception's message text and stack trace depend on the runtime, so they are not printed.

```kotlin
// Main.kt — not-null assertion
fun main() {
    val present: String? = "ada"
    val missing: String? = null
    println(present!!.length)
    try {
        println(missing!!.length)
    } catch (e: NullPointerException) {
        println(e::class.simpleName)
    }
}
```

```
3
NullPointerException
```

`3` is printed for the filled-in value and `NullPointerException` for the empty one. The difference from the safe call can be read from the output: a safe call returns `null` for `null` and lets the program continue, while the assertion stops the program with an exception in the same situation. The assertion moves the error to a point the programmer has declared; the protection the compiler offers is lifted on that line.

**TN8.** The exception was caught only on a local variable and on a direct `length` call; the behavior of the assertion on a property or a function return value was not tried. The appearance of the name `NullPointerException` in this output is a measurement that depends on Kotlin 2.4.21's JVM target.

#### The Same Expression Twice

For the course's measurement axis, the real question is this: if the same safe call expression is run twice, what is the relationship between the results? If the receiver is filled in, `uppercase` produces a separate string on each call; the two results are equal but not the same object. If the receiver is `null`, both expressions give `null`, and `null` with `null` is the same object. The block below measures these two cases. The third value differs from the twin by a single letter: `ADI` is not `ADA`.

```kotlin
// Main.kt — same expression twice
fun main() {
    val name: String? = buildString { append("a"); append("da") }
    val missing: String? = null
    val checks = listOf(
        Criterion("full", name?.uppercase(), name?.uppercase(), "ADI"),
        Criterion("empty", missing?.uppercase(), missing?.uppercase(), "ADI"),
    )
    for (check in checks) check.report()
    summarize(checks)
}
```

```
full: same-object=false, twin=true, other=false -> content
empty: same-object=true, twin=true, other=false -> singleton
singleton: 1
identity: 0
content: 1
partial-content: 0
```

The full line has `same-object=false` and `twin=true`; because the third value differs, `other=false` comes out. The criterion is content: the two `ADA` values are separate objects, but `==` finds them equal. In the empty line, the same expression gives `null` and the criterion is singleton. The safe call itself does not determine a criterion; whether the receiver is filled in or empty does.

**TN9.** This measurement is for `uppercase`; whether two calls on the same receiver always produce separate objects was not tried for other string methods. The separate-object result in the full line was read from the `===` comparison; whether the runtime shares similar strings was not measured separately.

#### Operators on Lists Containing null

Operators are not limited to a single value. In a list that carries `null` elements, `?.` and `?:` are applied to each element separately. The block tries transformation, filtering, and summing functions on a `List<String?>`: `map` produces one result for each element, while `mapNotNull` and `filterNotNull` discard `null` results. The last two lines search for the `null` value itself.

```kotlin
// Main.kt — operators on a list containing null
fun main() {
    val names: List<String?> = listOf("ab", null, "cde")
    println(names.map { it?.length })
    println(names.mapNotNull { it?.length })
    println(names.filterNotNull().size)
    println(names.sumOf { it?.length ?: 0 })
    println(names.firstOrNull { it == null })
    println(names.indexOf(null))
}
```

```
[2, null, 3]
[2, 3]
2
5
null
1
```

`map` produces one result for each element, so the `null` element turns into a `null` result through `?.length` and the list stays as `[2, null, 3]`. `mapNotNull` makes the same transformation and discards the `null` results, so the output is `[2, 3]`. `filterNotNull` only filters, makes the type `List<String>`, and two elements remain. The `?: 0` inside the `sumOf` block replaces the missing length with zero, and the total comes out as `5`.

The last two lines search for the `null` value itself. `firstOrNull` gives the first element that satisfies the condition; even if the `null` element is found, the result is `null`, so this output cannot be told apart from the search failing. `indexOf(null)` reports the position and returns `1`. A common mistake is to read a `null` result from `firstOrNull` as "there is no such element". If the list can carry `null` elements, a position or a function such as `any` is chosen to ask about presence.

#### Operator Precedence

Because the Elvis operator has lower precedence than the addition operator, an expression written without parentheses can behave differently from how it looks. The block writes the same expression in two forms: with an addition on the right side, and with the left side wrapped in parentheses. It tries this with a filled-in and an empty value. Then `?.` and `?:` are used together, and both `?:` and `?.plus` are run on an `Int?`.

```kotlin
// Main.kt — operator precedence
fun main() {
    val missing: String? = null
    val present: String? = "x"
    println(present ?: "a" + "b")
    println((present ?: "a") + "b")
    println(missing ?: "a" + "b")
    println((missing ?: "a") + "b")
    println(missing?.length ?: -1)
    println(missing?.length.toString())
    val count: Int? = null
    println((count ?: 0) + 1)
    println(count?.plus(1))
}
```

```
x
xb
ab
ab
-1
null
1
null
```

The first line shows operator precedence. Because the Elvis operator has lower precedence than addition, `present ?: "a" + "b"` is read as `present ?: ("a" + "b")`. Since the left side is filled in, the right side is never computed and the result is `x`. When it is written with parentheses as `(present ?: "a") + "b"`, `xb` is obtained. When the left side is `null`, the two forms give the same result, `ab`, so the mistake shows up only with a filled-in value and can slip past tests.

In the following lines, `missing?.length ?: -1` gives `-1`. The line below is trickier: `missing?.length.toString()` prints `null`, but this `null` is a string. The `toString` call at the end of the chain runs on an `Int?` receiver and produces the string `"null"`; it does not short-circuit. The last two lines show the same distinction on an `Int?`: `(count ?: 0) + 1` makes the number `1`, while `count?.plus(1)` leaves it `null`. To produce a number out of a missing value, choose Elvis; to preserve the absence, choose `?.`.

#### The Bounding Measurement

This lesson's rule is that a safe call does not give a single criterion. The same holds for the Elvis operator, but this time the criterion is determined not by the operator itself but by its right side. The left side is `null` in all three observations, and the expression evaluates the right side every time. The right side is written in three different forms: an object declaration (its details are in a later lesson of this course), a freshly built instance of a class whose `equals` has not been written, and a string built with `buildString` every time.

```kotlin
// Main.kt — by right side
object Fallback
class Wrapper(val value: Int)

fun main() {
    val nothing: String? = null
    val checks = listOf(
        Criterion("object", nothing ?: Fallback, nothing ?: Fallback, Wrapper(1)),
        Criterion("class", nothing ?: Wrapper(0), nothing ?: Wrapper(0), Wrapper(1)),
        Criterion("string", nothing ?: buildString { append("em"); append("pty") },
            nothing ?: buildString { append("em"); append("pty") }, "full"),
    )
    for (check in checks) check.report()
    summarize(checks)
}
```

```
object: same-object=true, twin=true, other=false -> singleton
class: same-object=false, twin=false, other=false -> identity
string: same-object=false, twin=true, other=false -> content
singleton: 1
identity: 1
content: 1
partial-content: 0
```

The same operator, the same `null` left side, three criteria. An object declaration gives the same instance on every evaluation, so the criterion is singleton. An ordinary class builds a separate instance on every evaluation, and because `equals` has not been written, the two instances do not count as equal: the criterion is identity. A string is a separate object every time, but because `String` is compared by content, the criterion is content. The sum of the three lines gives a 1/1/1 distribution; the operator did not change, the right side did.

This can lead to an unexpected result when using the Elvis operator. When `nothing ?: Wrapper(0)` is written, the expression produces a separate instance on every run; code that compares the results of two runs with `==` will never find them equal, unless `Wrapper`'s `equals` is written. Equality comes from the type of the right side.

**TN10.** The bounding measurement was made with only three observations whose left side is `null`; that the right side is not evaluated when the left side is filled in was taken from the measurement in the Elvis section and is not repeated here. The result of writing the class's `equals` is the subject of the next lesson and was not measured here.

#### Summary

- A safe call does not make the call if the receiver is `null`, does not evaluate the argument, and gives `null` as the result; the chain breaks at the first `null` value.
- The right side of the Elvis operator is evaluated only when the left side is `null`; the right side can be `return` or `throw`.
- The not-null assertion takes the check away from the compiler; on an empty value a `NullPointerException` is thrown at run time.
- The same safe call expression gives two separate but equal strings on a filled-in receiver and two `null` values on an empty receiver: the first is content, the second singleton.
- The right side determines the criterion of the Elvis operator: an object declaration is singleton, a class without `equals` is identity, a string is content.
- This lesson's criterion: **content**. When the same safe call is run twice, a filled-in receiver gives two separate but equal strings.

#### Next Step

On the right side of the Elvis operator, a class whose `equals` has not been written turned the criterion into identity. The next lesson measures how such classes are written and where `equals` comes from: the primary constructor, the `init` block, the secondary constructor, and properties. Why two instances with the same values, built by two different routes, do not count as equal is explained there by looking at the criterion.

### Classes and Constructors

Source: https://academia.sh/en/courses/kotlin/classes-and-constructors

A measurement of the execution order of the primary constructor, the init block, and the secondary constructor, and of why instances of a class without equals fall to the identity criterion.

The previous lesson showed that a class without `equals` on the right side of the Elvis operator turned the criterion into identity. This lesson looks at how those classes are written: the primary constructor, the `init` block, the secondary constructor, and properties. First it measures which part runs when, then it explains through the criterion why two instances with the same values are not counted as equal.

The measurement axis stays the same: the relationship between a value and another value that counts as equal to it, the **criterion**. The `Criterion` class is given again below, as it was in the opening lesson. The rule works in order: if the first value and its twin are the same object, the criterion is singleton; if not, and the twin is not equal, it is identity; if the twin is equal but the third value is not, it is content; if the third value is equal too, it is partial content. This lesson measures the identity slice and moves on to content and partial content in the bounding measurement.

```kotlin
// Criterion.kt — classifies the criterion that makes two values equal
class Criterion(val label: String, first: Any?, twin: Any?, other: Any?) {
    val sameObject = first === twin
    val equalTwin = first == twin
    val equalOther = first == other

    val name: String = when {
        sameObject -> "singleton"
        !equalTwin -> "identity"
        !equalOther -> "content"
        else -> "partial-content"
    }

    fun report() {
        println("$label: same-object=$sameObject, twin=$equalTwin, other=$equalOther -> $name")
    }
}

fun summarize(checks: List<Criterion>) {
    for (name in listOf("singleton", "identity", "content", "partial-content")) {
        println("$name: ${checks.count { it.name == name }}")
    }
}
```
#### The Execution Order of Constructors

In Kotlin, the primary constructor of a class is written in the class header: `class Member(val name: String, val level: Int)`. The `val` in front of a parameter defines both the parameter and a property of the same name. The body can contain properties with initializers and `init` blocks; these behave like the body of the primary constructor and run in the order they are written in the file. A secondary constructor is written with the `constructor` keyword and has to delegate to the primary constructor with `: this(...)`. The constructor it delegates to runs first, and the secondary constructor's own body runs last.

The block below makes this order readable from the output. The `trace` function prints a line every time it is called. The initializer of the `title` property calls `trace`, the `init` block prints a line, and the body of the secondary constructor prints a line. `Member("ada")` calls the secondary constructor, and `Member("can", 2)` calls the primary constructor directly.

```kotlin
// Main.kt — constructor order
fun trace(label: String): String {
    println("step: $label")
    return label
}

class Member(val name: String, val level: Int) {
    val title = trace("property")

    init {
        println("init: $name")
    }

    constructor(name: String) : this(name, 1) {
        println("secondary: $name")
    }
}

fun main() {
    val first = Member("ada")
    println(first.level)
    val second = Member("can", 2)
    println(second.level)
}
```

```
step: property
init: ada
secondary: ada
1
step: property
init: can
2
```

In both constructions, the `step: property` line comes before the `init: ...` line, because `title` is written before the block. The secondary constructor runs only in the first construction, and the `secondary: ada` line is printed after the `init: ada` line: the primary constructor and its initializers are completed before the body of the secondary constructor. There is no `secondary` line in the second construction, because the primary constructor was called directly.

**TN11.** This measurement shows the order only for this class. Swapping the initializer and the `init` block changes the output; the order is the order of writing. Which initializer comes before which `init` block depends on how the measured class is written.

#### Properties, Default Values, and Accessors

The `val` and `var` parameters in the primary constructor become properties. Properties also have a computed kind: `val area: Int get() = width * height` is not stored in a field and is computed every time it is read. Parameters can be given default values, and the caller can also pass parameters by name, out of order. If a custom `set` is written for a `var` property, the assigned value can be processed before it goes into the field; the field itself is reached by the name `field`.

The block below measures three things. `Rectangle(3)` leaves the height at the default value 1. `Rectangle(height = 2, width = 5)` passes the parameters by name, out of order. Finally, a string with spaces at the start and end is assigned to the `label` property; `set` strips those spaces.

```kotlin
// Main.kt — properties
class Rectangle(val width: Int, val height: Int = 1) {
    val area: Int get() = width * height

    var label: String = "unnamed"
        set(value) {
            field = value.trim()
        }
}

fun main() {
    val narrow = Rectangle(3)
    println(narrow.area)
    val wide = Rectangle(height = 2, width = 5)
    println(wide.area)
    wide.label = "  box  "
    println("[" + wide.label + "]")
}
```

```
3
10
[box]
```

The areas were computed as 3 times 1 and 5 times 2. The label was stored not as it was assigned but after passing through `set`: the square brackets show that no spaces remain.

**TN12.** `area` is not stored in a field. The measured block shows only the result of reading; that `area` would change if `width` and `height` were mutable does not follow from this block. This was measured only for `val` values that come from the constructor.

#### A Class Without Equals

Now back to the criterion. The `Coordinate` class has two constructors: the primary constructor takes two numbers, and the secondary constructor splits a string of the form `"3,4"` and delegates to the same primary constructor. The class has no `equals`. In Kotlin every class derives from `Any`, and `Any`'s `equals` method looks at whether two references point to the same object. Unless the class overrides it, the `==` operator looks at identity too.

```kotlin
// Coordinate.kt — a class without equals
class Coordinate(val x: Int, val y: Int) {
    constructor(text: String) : this(text.substringBefore(",").toInt(), text.substringAfter(",").toInt())
}
```

The measurement consists of three lines. In the first, the first two of the three values are the same object. In the second, two separate `Coordinate(3, 4)` are built from the primary constructor. In the third, the twin is built from the secondary constructor, that is, from the string; the values are 3 and 4 again.

```kotlin
// Main.kt — instances built by two routes
fun main() {
    val same = Coordinate(1, 2)
    val checks = listOf(
        Criterion("same-object", same, same, Coordinate(1, 3)),
        Criterion("same-route", Coordinate(3, 4), Coordinate(3, 4), Coordinate(3, 5)),
        Criterion("two-routes", Coordinate(3, 4), Coordinate("3,4"), Coordinate(3, 5)),
    )
    for (check in checks) check.report()
    summarize(checks)
}
```

```
same-object: same-object=true, twin=true, other=false -> singleton
same-route: same-object=false, twin=false, other=false -> identity
two-routes: same-object=false, twin=false, other=false -> identity
singleton: 1
identity: 2
content: 0
partial-content: 0
```

In the first line the rule stops at the first question: because it is the same object, the criterion is singleton. In the second and third lines, `twin=false` comes out even though the fields are exactly the same, and the criterion is identity. The construction route, whether primary or secondary, does not change the result. What makes two instances with the same values equal is not the class's fields; it is what `equals` does.

This lesson's measurement has to be completed with a distinction. What is measured here is whether two instances are counted as equal to each other. Whether an instance is shared from somewhere else, or whether a change is visible through another reference, is a separate question and was not measured in this lesson. The identity criterion says only this: `==` gives `false` for two separate objects.

**TN13.** The measurement is only for `Coordinate` and the two constructor routes. The same result was not measured for a class whose fields are of other types, or for a class derived from a superclass other than `Any`. If the superclass has overridden `equals`, the picture may be different.

#### Constructor Order in Inheritance

When a class derives from another class, one more link is added to the constructor order. Kotlin classes are closed by default; the class to be derived from is marked `open`, and the subclass calls the superclass's constructor in the header line. In the block below, both the superclass and the subclass have an `init` block. Which one runs first is read from the output.

```kotlin
// Main.kt — superclass constructor
open class Base(val name: String) {
    init {
        println("Base init: $name")
    }
}

class Derived(name: String, val extra: Int) : Base(name) {
    init {
        println("Derived init: $extra")
    }
}

fun main() {
    val item = Derived("ada", 3)
    println(item.name + item.extra)
}
```

```
Base init: ada
Derived init: 3
ada3
```

The output shows the order clearly: the `Base init: ada` line comes before the `Derived init: 3` line. The `Base(name)` call in the subclass's header line builds the superclass before the subclass's own initializers run. The reason is simple: the subclass's state can depend on the superclass's state, so the side being depended on has to be ready first. The last line shows that the `name` value comes from the superclass and the `extra` value from the subclass, and that both are reached through a single object.

This order extends the execution order in the previous section. In a single class, the order is property initializers, `init` blocks, and the secondary constructor body; in inheritance, the whole of the superclass is added in front of this. If a property of the subclass is touched inside the superclass's constructor, that property has not been set up yet; for this reason superclass constructors do not read state that belongs to the subclass. The limit of responsibility is visible in this output: each class sets up its own fields only in its own `init` block.

#### Computed on Every Read and a One-Time Initializer

It was said earlier that the `area` property is not stored in a field and is computed every time it is read. This has a measurable consequence: how many times is the computing function called? In the block, two properties call the same function. One is defined with an initializer, the other with `get()`. Both are read three times; the counters keep how many times the function ran.

```kotlin
// Main.kt — computed on every read
var storedCalls = 0
var computedCalls = 0

fun compute(kind: String): Int {
    if (kind == "stored") storedCalls++ else computedCalls++
    return 10
}

class Holder {
    val stored: Int = compute("stored")
    val computed: Int get() = compute("computed")
}

fun main() {
    val holder = Holder()
    println(holder.stored + holder.stored + holder.stored)
    println(holder.computed + holder.computed + holder.computed)
    println("stored=$storedCalls computed=$computedCalls")
}
```

```
30
30
stored=1 computed=3
```

The last line of the output gives the counters: `stored=1 computed=3`. Both properties were read three times and both gave the total `30`, but the computing function ran a different number of times. The property defined with an initializer computes the value once, while the object is being built, and stores it in a field. The property defined with `get()` carries no field; the function runs again on every read.

Since both results return the same value, the difference is visible only from the counter, not from the output. For this reason, a property defined with `get()` suits values that are cheap and have to be up to date every time: a field derived from other properties, or a total. If an expensive computation or a function with a side effect is put into `get()`, every read pays that cost and the side effect is repeated as many times as there are reads. If the computation has to be done once, the initializer is chosen; if it has to be up to date every time, the accessor is chosen.

#### A Written Equals and Other Types

A hand-written `equals` does not meet only values of the same type. Because its parameter is `Any?`, `null` and unrelated types can come in as well. The `other is ValueCoordinate` check we write filters out these cases. The block calls `equals` four times: with `null`, with a string, with another instance with the same values, and with the object itself.

```kotlin
// Main.kt — equals and other types
class ValueCoordinate(val x: Int, val y: Int) {
    override fun equals(other: Any?): Boolean =
        other is ValueCoordinate && x == other.x && y == other.y

    override fun hashCode(): Int = 31 * x + y
}

fun main() {
    val point = ValueCoordinate(1, 2)
    val anything: Any = "1,2"
    println(point.equals(null))
    println(point.equals(anything))
    println(point == ValueCoordinate(1, 2))
    println(point.equals(point))
}
```

```
false
false
true
true
```

All four calls return a `Boolean`, and the output is `false`, `false`, `true`, `true`. `equals(null)` gives `false`, because the `is` check fails for `null`. When the string is passed as an `Any`, `false` comes out too; because the types do not match, the fields are not even looked at. On a separate instance with the same values `true` returns, and on the object itself `true` returns again.

The parameter being `Any?` is a contract: `equals` can meet any kind of value and must say `false` without throwing an exception. A common mistake is to assume the parameter is `ValueCoordinate` and convert it directly, or to skip the type check and reach for the fields. The correct pattern is the `is` check first; thanks to the `&&` chain, the field comparison is made only after the check passes, and the compiler sees the `other` on the right side as a `ValueCoordinate`. `hashCode` is computed from the same fields; equal objects must produce equal hash codes, otherwise hash-based collections work inconsistently.

#### The Bounding Measurement

If the written `equals` determines equality and not the class's fields, classes with the same fields should fall to different criteria depending on `equals`. The bounding measurement tests this with three lines. `ValueCoordinate` writes an `equals` that looks at both fields and a `hashCode` consistent with it. `LeftCoordinate` looks only at the `x` field. Of the three lines, the first tries full equality, the second the case where `x` is the same and `y` differs, and the third the case where `x` differs. The two lines at the end of the block put two instances with the same values into a set with `hashSetOf` and print the size of the set; this is how it observes that `hashCode` is written along with `equals`. The `hashCode` values themselves are not printed.

```kotlin
// Main.kt — once equals is written
class ValueCoordinate(val x: Int, val y: Int) {
    override fun equals(other: Any?): Boolean =
        other is ValueCoordinate && x == other.x && y == other.y

    override fun hashCode(): Int = 31 * x + y
}

class LeftCoordinate(val x: Int, val y: Int) {
    override fun equals(other: Any?): Boolean = other is LeftCoordinate && x == other.x

    override fun hashCode(): Int = x
}

fun main() {
    val checks = listOf(
        Criterion("full", ValueCoordinate(3, 4), ValueCoordinate(3, 4), ValueCoordinate(3, 5)),
        Criterion("x-only-y-differs", LeftCoordinate(3, 4), LeftCoordinate(3, 4), LeftCoordinate(3, 5)),
        Criterion("x-only-x-differs", LeftCoordinate(3, 4), LeftCoordinate(3, 4), LeftCoordinate(2, 4)),
    )
    for (check in checks) check.report()
    summarize(checks)
    println(hashSetOf(Coordinate(3, 4), Coordinate(3, 4)).size)
    println(hashSetOf(ValueCoordinate(3, 4), ValueCoordinate(3, 4)).size)
}
```

```
full: same-object=false, twin=true, other=false -> content
x-only-y-differs: same-object=false, twin=true, other=true -> partial-content
x-only-x-differs: same-object=false, twin=true, other=false -> content
singleton: 0
identity: 0
content: 2
partial-content: 1
2
1
```

The same class pattern gives two criteria across three lines. Because `ValueCoordinate` compares both fields, the twin and the other value separate: content. `LeftCoordinate` comes out equal even when `y` differs, so the third value gives the same result as the twin and the criterion falls to partial content; when `x` differs, content comes out. Two separate lines of the same class give two separate criteria; so the criterion is determined not by a class but by the fields `equals` looks at and the pair of values being compared, together. In the sets, `Coordinate` stays as two separate elements, and `ValueCoordinate` comes down to a single element.

**TN14.** This measurement is for a small class with two fields. In a class with more fields, which fields enter `equals` is specific to each class; the same behavior cannot be assumed without measuring.

#### Summary

- The primary constructor is written in the class header; initializers and `init` blocks run in the order they are written, and the secondary constructor's body runs last.
- A secondary constructor has to delegate to the primary constructor with `: this(...)`, and the constructor it delegates to completes first.
- Properties can be given behavior with default values, named arguments, a computed read, and a custom `set`.
- Instances with the same values of a class without `equals` fall to the identity criterion, whichever constructor they were built with.
- This lesson's criterion: **identity**. In the bounding measurement the same class pattern fell to content and to partial content as well, depending on `equals`.

#### Next Step

To make a class behave like a value, `equals`, `hashCode`, and `toString` had to be written by hand. The next lesson measures data classes, which generate these three from the properties in the primary constructor: it looks at whether a separate object built with `copy` is counted as equal, and at what a destructuring declaration does.

### Data Classes

Source: https://academia.sh/en/courses/kotlin/data-classes

A measurement of the methods a data class generates from the properties in its primary constructor, of the separate object built with copy, and of how the type of a field affects the criterion.

The previous lesson showed that instances with the same values of a class without `equals` fall to the identity criterion, and that they move to content once `equals` and `hashCode` are written by hand. Writing those two methods by hand in every class is tedious and error-prone. Kotlin leaves this job to the compiler with the `data class` keyword: it generates the `equals`, `hashCode`, `toString`, `copy`, and `componentN` methods from the properties in the primary constructor. This lesson measures what the generated methods do and how they build the criterion.

The measurement axis stays the same: the relationship between a value and another value that counts as equal to it, the **criterion**. The `Criterion` class is given again below, as it was in the opening lesson. The rule works in order: if the first value and its twin are the same object, the criterion is singleton; if not, and the twin is not equal, it is identity; if the twin is equal but the third value is not, it is content; if the third value is equal too, it is partial content. This lesson measures the content slice.

```kotlin
// Criterion.kt — classifies the criterion that makes two values equal
class Criterion(val label: String, first: Any?, twin: Any?, other: Any?) {
    val sameObject = first === twin
    val equalTwin = first == twin
    val equalOther = first == other

    val name: String = when {
        sameObject -> "singleton"
        !equalTwin -> "identity"
        !equalOther -> "content"
        else -> "partial-content"
    }

    fun report() {
        println("$label: same-object=$sameObject, twin=$equalTwin, other=$equalOther -> $name")
    }
}

fun summarize(checks: List<Criterion>) {
    for (name in listOf("singleton", "identity", "content", "partial-content")) {
        println("$name: ${checks.count { it.name == name }}")
    }
}
```
#### The Generated Methods

`data class Ticket(val id: Int, val owner: String)` is a single line and has no body. The block below tries the generated methods in turn. `println(ticket)` shows the generated `toString`. The two comparisons look at `equals`: against a separate `Ticket` with the same values, and against a `Ticket` with a different `owner`. The fourth line asks whether the `hashCode` values of two equal objects are equal; the value itself is not printed. The last two lines try `copy`: the copy built by changing `owner`, and the first object itself.

```kotlin
// Main.kt — generated methods
data class Ticket(val id: Int, val owner: String)

fun main() {
    val ticket = Ticket(7, "ada")
    println(ticket)
    println(ticket == Ticket(7, "ada"))
    println(ticket == Ticket(7, "can"))
    println(ticket.hashCode() == Ticket(7, "ada").hashCode())
    val moved = ticket.copy(owner = "can")
    println(moved)
    println(ticket)
}
```

```
Ticket(id=7, owner=ada)
true
false
true
Ticket(id=7, owner=can)
Ticket(id=7, owner=ada)
```

`toString` prints the fields with their names. `equals` finds the separate object with the same values equal and the one with a different `owner` not equal, and `hashCode` comes out equal for equal objects. `copy(owner = "can")` builds a separate object; the first object stays unchanged.

**TN15.** The measurement is for a small data class with two fields. Whether the generated methods behave the same in a class with many fields, or when one of the fields is another data class, does not follow from this block.

#### Copy and the Criterion

Now the same methods are tied to the criterion. There are three lines. In the first, the first two of the three values are the same object. In the second, the twin is built with `ticket.copy()`, that is, with no field changed. In the third, the twin is built by calling the primary constructor directly. In every line, the third value is a `Ticket` whose `id` or `owner` differs.

```kotlin
// Main.kt — copy and the criterion
data class Ticket(val id: Int, val owner: String)

fun main() {
    val ticket = Ticket(7, "ada")
    val checks = listOf(
        Criterion("same-object", ticket, ticket, ticket.copy(id = 8)),
        Criterion("copy", ticket, ticket.copy(), ticket.copy(id = 8)),
        Criterion("constructor", ticket, Ticket(7, "ada"), Ticket(7, "can")),
    )
    for (check in checks) check.report()
    summarize(checks)
}
```

```
same-object: same-object=true, twin=true, other=false -> singleton
copy: same-object=false, twin=true, other=false -> content
constructor: same-object=false, twin=true, other=false -> content
singleton: 1
identity: 0
content: 2
partial-content: 0
```

In the first line the rule stops at the first question: because it is the same object, the criterion is singleton. In the second and third lines the twin is a separate object, `twin=true` comes out, and because the third value is not equal, the criterion is content. There is no difference in criterion between the object built with `copy()` and the object built with the constructor. `Coordinate` in the previous lesson, by contrast, gave identity in the same two lines. The difference is not in the construction route but in who wrote `equals`.

This lesson's distinction is here too: what is measured is not that `copy` exists as a method, but which criterion gives the equality. `copy` builds a separate object; that object being counted as equal comes from the generated `equals`.

**TN16.** The measurement is only for `Ticket`. `copy()` builds a shallow copy; whether two copies share a field when the field is a mutable object was not measured in this block.

#### The Destructuring Declaration and componentN

The methods a data class generates, such as `component1` and `component2`, make the destructuring declaration possible: the line `val (id, owner) = ticket` assigns a `componentN` call to each name in order. The order is the parameter order in the primary constructor. The block below tries the declaration and two `componentN` calls. Then it asks which fields the generated methods look at: in the `Stamped` class, the `note` field is defined in the body, not in the primary constructor. Two `Stamped` objects are built with the same constructor values but different `note` values and compared.

```kotlin
// Main.kt — destructuring and componentN
data class Ticket(val id: Int, val owner: String)

data class Stamped(val id: Int, val owner: String) {
    var note: String = ""
}

fun main() {
    val ticket = Ticket(7, "ada")
    val (id, owner) = ticket
    println("$id $owner")
    println(ticket.component1())
    println(ticket.component2())
    val first = Stamped(1, "ada")
    first.note = "a"
    val second = Stamped(1, "ada")
    second.note = "b"
    println(first == second)
    println(first)
}
```

```
7 ada
7
ada
true
Stamped(id=1, owner=ada)
```

The declaration gives `7 ada`. `component1()` and `component2()` return the same values directly. `first == second` comes out `true`, even though the `note` fields are `a` and `b`. `toString` does not print `note` either. The generated methods look only at the properties in the primary constructor; a property in the body takes no part in equality or in the output.

**TN17.** This was measured only for a property defined with `var` in the body. Whether a computed property defined in the body, or a property inherited from a superclass, has a separate behavior was not measured.

#### Copy Is Shallow

`copy` copies the fields, but if a field is a mutable collection, what gets copied is not the collection itself but the reference to it. In the block, `Basket` carries a `MutableList`. When an item is added to the first copy, is the first object affected? In the second attempt the list is copied explicitly and the same question is asked again.

```kotlin
// Main.kt — copy is shallow
data class Basket(val owner: String, val items: MutableList<String>)

fun main() {
    val first = Basket("ada", mutableListOf("apple"))
    val second = first.copy(owner = "can")
    second.items.add("pear")
    println(first.items)
    println(second.items)
    println(first.items === second.items)
    val deep = first.copy(items = first.items.toMutableList())
    deep.items.add("cherry")
    println(first.items)
    println(deep.items)
}
```

```
[apple, pear]
[apple, pear]
true
[apple, pear]
[apple, pear, cherry]
```

The first two lines of the output are the same, `[apple, pear]`. The `pear` added through `second` is visible inside `first` too. The third line says why: `first.items === second.items` gives `true`, so the two objects go to the same list. `copy` copies the fields one by one, but for the list field, what is copied is not the list itself but the reference to it. The copying is shallow.

In the last two lines the list is copied explicitly with `toMutableList()`. This time the `cherry` added to `deep` is not visible in `first`. A common mistake is to take `copy` for a fully independent copy. In a data class that carries a mutable collection, a `copy` call preserves the equality semantics on the one hand, and on the other makes the two objects owners of the same state. The fix is either to make the field a read-only `List` or to copy the collection explicitly in the `copy` call. The content criterion holds here too: the two objects are equal as far as `equals` is concerned, but when one changes, the other changes as well.

#### Use as a Key and a Common Mistake

The generated `hashCode` makes a data class usable as a map key and a set element. A lookup with a separate object with the same values finds it. But if a data class with a `var` field is modified after it enters a set, its hash code changes and the object is separated from the place where it sits in the set. The block first reads from a map with a key with the same values, then changes the field of an object placed in a set and repeats the lookup.

```kotlin
// Main.kt — a data class as a key
data class Ticket(val id: Int, var owner: String)

fun main() {
    val prices = hashMapOf(Ticket(1, "ada") to 10)
    println(prices[Ticket(1, "ada")])
    val key = Ticket(2, "can")
    val set = hashSetOf(key)
    println(set.contains(key))
    key.owner = "eda"
    println(set.contains(key))
    println(set.size)
    println(set.contains(Ticket(2, "can")))
    println(set.any { it == key })
}
```

```
10
true
false
1
false
true
```

Reading from the map gives `10`: a separate `Ticket` with the same values is found as a key, because equality and the hash code come from the field values. For the object placed in the set, `contains` first returns `true`. After `key.owner` is changed, a lookup with the same object becomes `false`, but `size` is still `1`: the object is in the set, it just cannot be found. The `Ticket(2, "can")` built with the old value cannot be found either, because the element in the set now carries the owner `eda` and is not equal. A linear `any` scan, on the other hand, finds the object.

The reason for the mechanism is this: a hash-based collection places an element according to the hash code it had at the moment it was added. When the field changes, the code changes and the lookup looks somewhere else. A common mistake is to leave a `var` field in a data class used as a key or a set element. The rule: the fields that enter equality for an object placed in a collection are not changed while that object is in the collection. Data classes that will be keys are written with `val` fields, and if a change is needed, a separate object is produced with `copy` and the old one is removed from the collection.

#### Destructuring in Loops

The destructuring declaration works not only in `val` lines but also in `for` loops and lambda parameters. An unused position is skipped with `_`. Map entries are opened the same way, because they provide `component1` and `component2`. The block tries all of these forms.

```kotlin
// Main.kt — destructuring in a loop
data class Ticket(val id: Int, val owner: String)

fun main() {
    val tickets = listOf(Ticket(1, "ada"), Ticket(2, "can"))
    for ((id, owner) in tickets) println("$id=$owner")
    for ((_, owner) in tickets) println(owner)
    val (first) = tickets[1]
    println(first)
    val prices = mapOf("apple" to 3, "pear" to 5)
    for ((name, price) in prices) println("$name:$price")
    println(tickets.map { (id, _) -> id })
}
```

```
1=ada
2=can
ada
can
2
apple:3
pear:5
[1, 2]
```

The first two loops print `1=ada`, `2=can`, and then only the owner names; `_` skips the first position. The line `val (first) = tickets[1]` calls for attention: `first` is the value in the first position (`component1`), that is, the `id`, and the output is `2`, not the first ticket. In the map loop, `(name, price)` opens the entries with `component1` and `component2`. The lambda parameter in the last line is opened the same way and gives `[1, 2]`.

The defining trait of destructuring is that it depends on order, not on field names. The `componentN` functions are numbered by the parameter order in the primary constructor. A common mistake is to break the ordering when adding a field to a class; if a field is inserted between `id` and `owner` in the constructor, the old destructuring lines still compile but read a different field. For this reason fields are added only at the end, and destructuring is used only in small, stable data classes. For the rest, the `ticket.owner` form is more durable, because it relies on the meaning of the name, not on the order.

#### Nested Data Classes

If a field of a data class is a data class too, the generated methods work in a nested way. `toString` opens the inner object as well, `equals` descends into the inner object's `equals`, and `copy` carries the inner object as it is. The block tries this with an inner field that can be `null`.

```kotlin
// Main.kt — nested data classes
data class Address(val city: String)
data class Person(val name: String, val address: Address?)

fun main() {
    val person = Person("ada", Address("izmir"))
    println(person)
    println(person == Person("ada", Address("izmir")))
    println(person.copy(address = null))
    println(person.copy(address = Address("ankara")) == person)
    println(Person("ada", null) == Person("ada", null))
}
```

```
Person(name=ada, address=Address(city=izmir))
true
Person(name=ada, address=null)
false
true
```

The generated `toString` opens the inner object as well: `Person(name=ada, address=Address(city=izmir))`. `equals` descends into the inner object's `equals`, so two separate `Person` objects with the same values come out equal (`true`). `copy(address = null)` changes only the specified field, and `null` is carried as a field without trouble; `toString` shows it as `address=null`. A copy built with a different city is not equal to the original object (`false`), while two people with a `null` address are equal (`true`).

This chained behavior holds as long as the inner object is a data class too. If the inner field is an ordinary class without `equals`, the equality of the outer object falls to the identity of that field: two outer objects with the same values again do not come out equal. A common mistake is to make only the outer class a data class and assume that this is enough for the whole tree. Equality is provided separately at every node of the tree. The content criterion of this lesson is carried to the outermost level only if the fields inside have the content criterion as well.

#### The Bounding Measurement

The generated `equals` compares the fields with `==`. It follows that the type of a field should affect the criterion, according to the field's own `equals`. The bounding measurement compares two data classes with the same shape: `Packet` carries an `IntArray`, and `Message` carries the same numbers in a `List<Int>`. In both lines the twin is built separately with the same values, and the third value differs only in its `id`. The last line of the block asks something different: if two `Packet` objects share the same `IntArray` instance, are they equal?

```kotlin
// Main.kt — by field type
data class Packet(val id: Int, val bytes: IntArray)
data class Message(val id: Int, val bytes: List<Int>)

fun main() {
    val checks = listOf(
        Criterion("array", Packet(1, intArrayOf(1, 2)), Packet(1, intArrayOf(1, 2)), Packet(2, intArrayOf(1, 2))),
        Criterion("list", Message(1, listOf(1, 2)), Message(1, listOf(1, 2)), Message(2, listOf(1, 2))),
    )
    for (check in checks) check.report()
    summarize(checks)
    val shared = intArrayOf(1, 2)
    println(Packet(1, shared) == Packet(1, shared))
}
```

```
array: same-object=false, twin=false, other=false -> identity
list: same-object=false, twin=true, other=false -> content
singleton: 0
identity: 1
content: 1
partial-content: 0
true
```

Two classes with the same shape give two separate criteria. `Packet` does not come out equal when it is built with two separate arrays that have the same content: the array's `equals` looks at identity, and the `equals` the data class generates compares the field with this `equals`. `Message`, on the other hand, gives content, because the list's `equals` looks at the content. The last line confirms this: when the same array instance is shared, `Packet` comes out equal too. The generated method inherits the equality of the field type; the criterion is determined not by the data class but by the types of the fields.

**TN18.** The measurement is only for `IntArray` and `List<Int>`. That other array or collection types follow the same rule was not measured.

#### Summary

- A `data class` generates `equals`, `hashCode`, `toString`, `copy`, and `componentN` from the properties in the primary constructor.
- `copy` builds a separate object; that object is counted as equal to the first one by the generated `equals`.
- The destructuring declaration corresponds to the `component1` and `component2` calls, and its order is the constructor order.
- Properties in the body do not enter the generated methods; the field type determines equality, with `IntArray` giving identity and `List<Int>` giving content.
- This lesson's criterion: **content**. In the bounding measurement, two data classes with the same shape fell to content and to identity depending on the field type.

#### Next Step

Classes alone are not enough to express that a value belongs to only one of a few closed forms. The next lesson measures enumerations with `enum class` and sealed hierarchies with `sealed interface`: it looks at how an exhaustive `when` rejects a missing branch and at which criterion an enum entry falls to.

### Sealed Types and Enumerations

Source: https://academia.sh/en/courses/kotlin/sealed-types-and-enumerations

A measurement of which criterion an enum entry, a data object, a data class, and an ordinary class fall to in the same sealed hierarchy, and of how an exhaustive when rejects a missing branch.

The previous lesson showed that a data class generates `equals` from the properties in its primary constructor and that the criterion depends on the field types. Every class measured so far, however many instances it had, was an open type: a separate instance can be built anywhere. For cases where a value can be only one of a few forms counted in advance, Kotlin offers two tools: enumerations with `enum class`, and sealed classes and interfaces with the `sealed` keyword. This lesson measures both and looks at how each one falls to the criterion.

The measurement axis stays the same: the relationship between a value and another value that counts as equal to it, the **criterion**. The `Criterion` class is given again below, as it was in the opening lesson. The rule works in order: if the first value and its twin are the same object, the criterion is singleton; if not, and the twin is not equal, it is identity; if the twin is equal but the third value is not, it is content; if the third value is equal too, it is partial content. This lesson brings the slices of the previous three lessons together in a single hierarchy.

```kotlin
// Criterion.kt — classifies the criterion that makes two values equal
class Criterion(val label: String, first: Any?, twin: Any?, other: Any?) {
    val sameObject = first === twin
    val equalTwin = first == twin
    val equalOther = first == other

    val name: String = when {
        sameObject -> "singleton"
        !equalTwin -> "identity"
        !equalOther -> "content"
        else -> "partial-content"
    }

    fun report() {
        println("$label: same-object=$sameObject, twin=$equalTwin, other=$equalOther -> $name")
    }
}

fun summarize(checks: List<Criterion>) {
    for (name in listOf("singleton", "identity", "content", "partial-content")) {
        println("$name: ${checks.count { it.name == name }}")
    }
}
```
#### Enumerations

An enumeration is a class all of whose instances are counted one by one in its definition. `enum class Level { LOW, MEDIUM, HIGH }` defines exactly three `Level` values, and a fourth cannot be built from outside. Each entry is the single instance of the class. `entries` gives all the entries in definition order, `ordinal` returns the position number starting from zero, and `valueOf` finds an entry from a string and throws an exception if it cannot.

The block below tries these. The `describe` function maps each entry to a string with `when` and has no `else` branch: the compiler knows that all three entries are covered. The block also asks with `===` whether the entry found with `valueOf("MEDIUM")` is the same object as the `Level.MEDIUM` written directly in the code. The exception thrown when an undefined name is given is caught, and only the class name is printed.

```kotlin
// Main.kt — enumeration
enum class Level { LOW, MEDIUM, HIGH }

fun describe(level: Level): String = when (level) {
    Level.LOW -> "low"
    Level.MEDIUM -> "medium"
    Level.HIGH -> "high"
}

fun main() {
    println(Level.entries)
    println(Level.HIGH.ordinal)
    println(Level.valueOf("MEDIUM"))
    println(Level.valueOf("MEDIUM") === Level.MEDIUM)
    println(describe(Level.LOW))
    try {
        Level.valueOf("EXTREME")
    } catch (e: IllegalArgumentException) {
        println(e::class.simpleName)
    }
}
```

```
[LOW, MEDIUM, HIGH]
2
MEDIUM
true
low
IllegalArgumentException
```

`entries` gives the three entries in order, `HIGH` is numbered 2 when counting from zero, and the entry found with `valueOf` is the same object as the one written directly. This is the first question of the criterion: the entry is its own twin, and the criterion is singleton. An undefined name throws `IllegalArgumentException`.

**TN19.** The measurement is only for an enumeration with three entries and no fields or methods. Whether the behavior stays the same when the entries have their own fields or methods, or when the enumeration implements an interface, was not measured.

#### The Sealed Interface and the Exhaustive When

`sealed interface Shape` is an interface for which all the types that can implement `Shape` are known in advance, in the same package and in the same compilation. The hierarchy below consists of three subtypes: `data object Empty` is an object declaration with a single instance, `data class Circle` is a data class, and `class Blob` is an ordinary class with no `equals` written. These three types gather the previous lessons into a single hierarchy. Because the compiler knows the full list of subtypes, it can check whether the `when (shape)` expression covers all of them.

```kotlin
// Main.kt — closed hierarchy
sealed interface Shape
data object Empty : Shape
data class Circle(val radius: Int) : Shape
class Blob(val size: Int) : Shape

fun describe(shape: Shape): String = when (shape) {
    Empty -> "empty"
    is Circle -> "circle " + shape.radius
    is Blob -> "blob " + shape.size
}

fun main() {
    val shapes = listOf(Empty, Circle(2), Blob(3))
    for (shape in shapes) println(describe(shape))
    println(Empty)
    println(Circle(2))
}
```

```
empty
circle 2
blob 3
Empty
Circle(radius=2)
```

Three branches cover three subtypes, and no `else` is written. `println(Empty)` prints just the name: a `data object` generates `toString` with the name of the object. `println(Circle(2))` prints the data class form with its fields. For an ordinary `object`, the default `toString` would give a name and a hash code; the `data` modifier makes it readable.

The real value of the exhaustive `when` lies elsewhere: if a subtype is added to the hierarchy and one of the branches is forgotten, the compiler rejects it. The fragment below leaves out the `Blob` branch. As in the opening lesson, the fragment was measured and not compiled; the compiler gave an error that shows the missing branch by name. The error text is not printed here.

```kotlin
sealed interface Shape
data object Empty : Shape
data class Circle(val radius: Int) : Shape
class Blob(val size: Int) : Shape

fun describe(shape: Shape): String = when (shape) {
    Empty -> "empty"
    is Circle -> "circle " + shape.radius
}

fun main() {
    println(describe(Empty))
}
```

**TN20.** The compile-time rejection was measured on Kotlin 2.4.21. Whether the same check applies when `when` is used not as an expression but only as a statement was not measured in this lesson.

#### Enumerations with Fields

Enumeration entries can carry fields and methods. The entries take constructor values in parentheses, the entry list ends with `;`, and the members follow. The block below tries an enumeration that carries a size and a code: it calls a method on an entry, transforms over `entries`, compares entries by position number, and looks up a code that does not exist.

```kotlin
// Main.kt — enumeration with fields
enum class Planet(val code: String, val size: Int) {
    SMALL("s", 1),
    MEDIUM("m", 5),
    LARGE("l", 9);

    fun isBigger(other: Planet): Boolean = size > other.size
}

fun main() {
    println(Planet.LARGE.isBigger(Planet.SMALL))
    println(Planet.entries.map { it.code })
    println(Planet.entries.maxByOrNull { it.size })
    println(Planet.MEDIUM.name + Planet.MEDIUM.ordinal)
    println(Planet.SMALL < Planet.LARGE)
    println(Planet.entries.firstOrNull { it.code == "x" })
    println(Planet.entries.firstOrNull { it.code == "x" }?.name ?: "none")
}
```

```
true
[s, m, l]
LARGE
MEDIUM1
true
null
none
```

`LARGE.isBigger(SMALL)` gives `true`; the method uses the entry's own `size` field. `entries` gives the entries in definition order, so the list of codes comes out as `[s, m, l]`. `maxByOrNull` finds the entry with the largest size and prints `LARGE`. Joining `name` and `ordinal` gives `MEDIUM1`: the position of the second entry is `1`. `SMALL < LARGE` gives `true`, because entries can be compared by position number. When a code that does not exist is searched for, the result is `null`; `?.name ?: "none"` turns it into `none`.

The enumeration here is a closed and fixed set of values, and each entry is a single instance. Adding a field means giving values on the line where the entry is defined; a member cannot be written until the entry list is ended with `;`. That the search returns `null` also shows the difference from `valueOf`: `valueOf` throws an exception for a name that does not exist, while `firstOrNull` carries the absence as a value. Which one to choose depends on whether the absence is an error or an expected situation.

#### An Else Branch Silences Exhaustiveness

The rejection of a missing branch by an exhaustive `when` is a guarantee, but the guarantee is lost the moment an `else` is written. The block below uses the same hierarchy that was rejected above and puts an `else` in place of the `Blob` branch. This time the compilation passes. What the block shows is what happens when a subtype is added later: that subtype silently falls into the `else` branch.

```kotlin
sealed interface Shape
data object Empty : Shape
data class Circle(val radius: Int) : Shape
class Blob(val size: Int) : Shape

fun describe(shape: Shape): String = when (shape) {
    Empty -> "empty"
    is Circle -> "circle"
    else -> "unknown"
}

fun main() {
    println(describe(Empty))
    println(describe(Circle(1)))
    println(describe(Blob(1)))
}
```

```
empty
circle
unknown
```

The output is `empty`, `circle`, `unknown`. The compilation passed, because `else` covers every remaining case. The `Blob` instance fell into the `unknown` branch; in the exhaustive version above, this situation was a compile error. The difference is that the `else` branch switches off the compiler's check for missing subtypes.

The problem appears not in today's code but in tomorrow's change. A subtype added to the hierarchy later silently falls into that branch in every `when` expression that has an `else` branch, and the compiler gives no warning. In an exhaustive expression, the same addition would have flagged every relevant `when` expression with a compile error. A common mistake is to add an `else` "just in case". The guarantee the sealed type provides comes precisely from not writing this branch. If a truly default behavior is needed, that behavior is modeled as an explicit subtype, and `else` does not take on this job silently.

#### The Sealed Class and Smart Casts

Besides the sealed interface there is the `sealed class`; the difference is that the class can carry constructor state. The subtypes are usually written inside the class. The `Outcome` below describes three states: a success that carries a value, a failure that carries a reason, and a pending state that carries nothing. The exhaustive `when` makes a smart cast to the subtype in each branch and reaches fields such as `outcome.value` directly.

```kotlin
// Main.kt — sealed class and smart cast
sealed class Outcome {
    data class Success(val value: Int) : Outcome()
    data class Failure(val reason: String) : Outcome()
    data object Pending : Outcome()
}

fun render(outcome: Outcome): String = when (outcome) {
    is Outcome.Success -> "value " + (outcome.value + 1)
    is Outcome.Failure -> "error " + outcome.reason.length
    Outcome.Pending -> "waiting"
}

fun main() {
    val outcomes = listOf(Outcome.Success(1), Outcome.Failure("fail"), Outcome.Pending)
    for (outcome in outcomes) println(render(outcome))
    println(Outcome.Pending === Outcome.Pending)
    println(outcomes.filterIsInstance<Outcome.Success>().size)
    println(outcomes.count { it is Outcome.Failure })
}
```

```
value 2
error 4
waiting
true
1
1
```

`sealed class` and `sealed interface` provide the same guarantee: the subtypes are known at compile time. The difference is that the class can carry constructor state and superclass behavior. In the output the three branches run: `value 2`, `error 4`, and `waiting`. In each branch the compiler narrows the `outcome` value to the subtype of that branch; this is why `outcome.value` and `outcome.reason` can be written directly, with no separate cast.

The fourth line asks about `Pending === Pending` and returns `true`: a `data object` is a single instance, so identity and content equality coincide. The last two lines filter the hierarchy with collection functions: `filterIsInstance` and counting with `is` both give `1`. Nested writing puts the subtype names in the form `Outcome.Success`. This makes it visible that the subtypes belong to the hierarchy and prevents another type with the same name from getting mixed in. The subtypes can also be written outside, as long as they are in the same package, but nested writing shows the reader where the exhaustiveness looks.

#### Enumerations in Collections

Because enumeration entries are singletons, using them as keys in collections is trouble-free. Because entries are `Comparable`, they are sorted by position number. The block counts, sorts, and deduplicates the repeated entries in a list, then asks the order of two entries with `compareTo`.

```kotlin
// Main.kt — enumeration in collections
enum class Level { LOW, MEDIUM, HIGH }

fun main() {
    val levels = listOf(Level.HIGH, Level.LOW, Level.HIGH, Level.MEDIUM)
    println(levels.groupingBy { it }.eachCount())
    println(levels.sorted())
    println(levels.distinct().size)
    println(Level.HIGH.compareTo(Level.LOW))
    println(Level.valueOf("LOW") == Level.LOW)
}
```

```
{HIGH=2, LOW=1, MEDIUM=1}
[LOW, MEDIUM, HIGH, HIGH]
3
2
true
```

`groupingBy { it }.eachCount()` counts the entries and keeps the order of first appearance: `{HIGH=2, LOW=1, MEDIUM=1}`. Because entries are singletons, they are safe to use as keys; the two `HIGH` values point to the same instance and are collected under a single key. `sorted` arranges the entries by position number, and `[LOW, MEDIUM, HIGH, HIGH]` comes out. `distinct` leaves three entries. `HIGH.compareTo(LOW)` gives `2` in this example, a value consistent with the difference of the position numbers. `valueOf("LOW") == LOW` is `true`, because the name returns the same instance.

The mechanism here is that an enumeration is both singleton and ordered. The ordering depends on the definition order, which means the position of the entries in the file is part of the behavior. A common mistake is to insert entries in the middle later and break code that relies on the ordering: when the position number changes, both the result of `sorted` and the value of `compareTo` change. In an enumeration where the ordering matters, a new entry is added at the end, or the order is kept in an explicit field and the comparison is made over that field.

#### The Bounding Measurement

The three subtypes are under the same sealed interface. The seal closes who can be a subtype, but it does not determine `equals`. The same hierarchy should therefore give more than one criterion at the same time. The bounding measurement tries this with four lines. The first line compares an enumeration entry with its twin found by `valueOf`. The next three lines try the `Shape` subtypes in turn: `Empty`, `Circle(1)`, and `Blob(1)`. For each subtype the twin is built separately with the same value, and the value of the third is different; for `Empty`, `Circle(1)` is given as the third value because `Empty` has no other instance.

```kotlin
// Main.kt — kind of the subtype
sealed interface Shape
data object Empty : Shape
data class Circle(val radius: Int) : Shape
class Blob(val size: Int) : Shape
enum class Level { LOW, MEDIUM, HIGH }

fun main() {
    val checks = listOf(
        Criterion("entry", Level.HIGH, Level.valueOf("HIGH"), Level.LOW),
        Criterion("data-object", Empty, Empty, Circle(1)),
        Criterion("data-class", Circle(1), Circle(1), Circle(2)),
        Criterion("class", Blob(1), Blob(1), Blob(2)),
    )
    for (check in checks) check.report()
    summarize(checks)
}
```

```
entry: same-object=true, twin=true, other=false -> singleton
data-object: same-object=true, twin=true, other=false -> singleton
data-class: same-object=false, twin=true, other=false -> content
class: same-object=false, twin=false, other=false -> identity
singleton: 2
identity: 1
content: 1
partial-content: 0
```

The same hierarchy gives three separate criteria. The enumeration entry and the `data object` are singletons because each has a single instance: the twin is already the same object. `Circle` gives content because it is a data class. `Blob` has no `equals` written; two instances with the same value are different objects, and the criterion is identity. The seal does not determine the criterion; the kind in which the subtype is written does. Singleton, identity, and content can stand side by side under a single interface handled by a single `when`. The distribution is 2 singleton, 1 identity, 1 content, 0 partial content.

#### Summary

- Enumeration entries are the single instances of the class: the entry found with `valueOf` is the same object as the one written directly, and the criterion is singleton.
- `entries`, `ordinal`, and `valueOf` list the entries in definition order, order them, and find them by name; an undefined name throws `IllegalArgumentException`.
- In a sealed interface the list of subtypes is known to the compiler; an exhaustive `when` rejects a missing branch at compile time.
- A `data object` gives singleton, a data class subtype gives content, and an ordinary class subtype with no `equals` written gives identity.
- This lesson's criterion: **singleton**. In the bounding measurement, the same sealed hierarchy gave all three criteria at once; the kind of the subtype, not the hierarchy, determines the criterion.

#### Next Step

`data object Empty` built a single-instance object with a declaration. In Kotlin, the general form of this pattern is the `object` declaration: it defines the class and its single instance together. The next lesson measures object declarations, the helper object inside a class, and the object expression built without giving it a name, and looks at how each of the three falls to the criterion.

### Object Declarations

Source: https://academia.sh/en/courses/kotlin/object-declarations

Measures the object declaration, the companion object, the `data object`, and the object expression with the criterion that makes two values equal, and shows that a declaration is a singleton while an expression builds a new object on every evaluation.

In the previous lesson, "Sealed Types and Enumerations", we saw that enumeration entries and `data object` values are singletons: reading the same entry twice returned the same object. This lesson goes down to the source of that singleton behavior. The `object` keyword shows up in Kotlin in three separate places: in a declaration, inside a class as a companion object, and at the start of an expression. All three are written with the same keyword; this lesson measures whether the three do the same thing.

The measure is the same question in every lesson of this course: what makes these two values equal? There are four possible answers: they are the same object (singleton), equality rests on identity (identity), their contents are equal (content), and only some of their contents are equal (partial content).

An **object declaration** defines a type and builds the single instance of that type at the same time. The name of the declaration is the name of both the type and that single value. The constructor is not called; writing the name is reading the value itself.

#### The Measurement Setup

`Criterion` stays in the first block, in the same form, in every lesson. Each observation takes three values: `first`, `twin` (a value built separately with the same recipe), and `other` (a value that differs in only one respect). The rule sees the result, not the reason: if `first === twin`, the criterion is `singleton`; if not, and `first == twin` is false, it is `identity`; if `first == other` is false, it is `content`; if none of these, it is `partial-content`.

```kotlin
// Criterion.kt — classifies the criterion that makes two values equal
class Criterion(val label: String, first: Any?, twin: Any?, other: Any?) {
    val sameObject = first === twin
    val equalTwin = first == twin
    val equalOther = first == other

    val name: String = when {
        sameObject -> "singleton"
        !equalTwin -> "identity"
        !equalOther -> "content"
        else -> "partial-content"
    }

    fun report() {
        println("$label: same-object=$sameObject, twin=$equalTwin, other=$equalOther -> $name")
    }
}

fun summarize(checks: List<Criterion>) {
    for (name in listOf("singleton", "identity", "content", "partial-content")) {
        println("$name: ${checks.count { it.name == name }}")
    }
}
```
**TN21.** In an object declaration `twin` cannot be built separately, because there is no second instance; in this lesson we gave as `twin` the value of the same declaration read a second time. As `other` we used a declaration with the same body but a different name.

#### A Declaration Is a Single Object

The block below writes two object declarations. `IdSource` keeps a counter; `OtherSource` is a separate declaration with the same body. `IdSource` is read into two separate variables, and the counter is advanced through these two variables and through its name directly.

```kotlin
// Main.kt — an object declaration read twice
object IdSource {
    private var next = 1
    fun take(): Int = next++
}

object OtherSource {
    private var next = 1
    fun take(): Int = next++
}

fun main() {
    val a = IdSource
    val b = IdSource
    println(a.take())
    println(b.take())
    println(IdSource.take())
    Criterion("object-declaration", a, b, OtherSource).report()
}
```

```
1
2
3
object-declaration: same-object=true, twin=true, other=false -> singleton
```

The three `take()` calls advanced the same counter, whichever variable they were made through, because `a` and `b` point to the same object. The rule wrote this as `singleton`: since `===` came out true, the equality of the twin was not even looked at. The counter of `OtherSource` is separate, so it is not equal to the first object.

Because a declaration is not a constructor call, putting parentheses after the name does not work either. We verified this first: the fragment below did not compile on Kotlin 2.4.21, which is why it stays without output.

```kotlin
object IdSource

fun main() {
    val source = IdSource()
}
```

**TN22.** We do not write the error text of the rejection in the lesson; it was only verified that the fragment does not compile. Whether a declaration can inherit from another class or implement an interface was not measured in this lesson.

#### The Companion Object and the Factory

A **companion object** is an object declaration written inside a class as `companion object`. It holds the members that can be called with the class name. Since it is a declaration, it is a singleton. But a factory function written inside it builds instances of the class, and those instances are not singletons. We measure the two side by side.

```kotlin
// Main.kt — the companion object is a singleton, the factory builds a new object every time
class Account private constructor(val owner: String) {
    companion object {
        fun open(owner: String): Account = Account(owner)
    }
}

fun main() {
    val checks = listOf(
        Criterion("companion-object", Account.Companion, Account.Companion, Any()),
        Criterion("factory-result", Account.open("ada"), Account.open("ada"), Account.open("can")),
    )
    for (check in checks) check.report()
    summarize(checks)
}
```

```
companion-object: same-object=true, twin=true, other=false -> singleton
factory-result: same-object=false, twin=false, other=false -> identity
singleton: 1
identity: 1
content: 0
partial-content: 0
```

The `companion-object` line came out `singleton` and the `factory-result` line came out `identity`. Two `object` counterparts written inside the same class gave two separate criteria: the companion object itself is one, and the accounts it produces are separate on every call. Even when the owners of two `Account` objects are the same, `==` did not return true, because `Account` is an ordinary class and does not redefine `equals`; equality falls to identity. In the language of the rule, this means `identity`. Making the constructor private (`private constructor`) narrowed the way of building instances to the function of the companion object; the factory thus became the only entry door.

**TN23.** That the factory builds a new `Account` on every call was measured here only for this class. It is also possible for a factory to return an instance built beforehand; in that case the criterion would be `singleton`, but we did not write such a factory in this lesson.

#### data object

A `data object` adds to an object declaration the readable `toString` and the `equals`/`hashCode` of a data class. We do not print the default `toString` output of a plain `object` in this course; a `data object`, on the other hand, writes only its name. This is the reason state objects in sealed hierarchies are written as `data object`: a readable name shows up in the output.

```kotlin
// Main.kt — a data object carries a readable toString
data object Accepted

fun main() {
    println(Accepted)
    println(Accepted == Accepted)
    val checks = listOf(Criterion("data-object", Accepted, Accepted, "Accepted"))
    for (check in checks) check.report()
}
```

```
Accepted
true
data-object: same-object=true, twin=true, other=false -> singleton
```

The first line of the output contains nothing but the name of the declaration. The criterion is `singleton` again: a `data object` does not produce a new instance, it only adds readability to the existing singleton object. The `"Accepted"` string we gave as `other` is not equal to `Accepted`; even though the two are spelled the same, their types are separate.

#### Creation on First Access and the Interface

When is the single instance of an object declaration built? When the program starts, or at the moment it is first used? The answer to this question shows up in the `init` block in the body of the declaration: whenever the block runs, the instance has been built at that time. A declaration can also implement an interface, that is, bind its behavior to a contract like an ordinary class. Finally, a declaration can be matched in a `when` expression like a constant.

In the block below, `Polite` is an object declaration that implements the `Greeter` interface and prints a line when it is built. `Idle` is a `data object`. The `describe` function matches a value in three ways: equality with `Idle`, a type check with `is Polite`, and everything else. `main` prints `started` on its first line; this way, where the `init` line shows up reveals at which moment the declaration was built.

```kotlin
// Main.kt — the object is built on first access, implements an interface, matches with when
interface Greeter {
    fun greet(name: String): String
}

object Polite : Greeter {
    init {
        println("polite created")
    }

    override fun greet(name: String): String = "hello " + name
}

data object Idle

fun describe(state: Any): String = when (state) {
    Idle -> "empty"
    is Polite -> "polite"
    else -> "other"
}

fun main() {
    println("started")
    val greeter: Greeter = Polite
    println(greeter.greet("ada"))
    println(Polite.greet("can"))
    println(describe(Idle))
    println(describe(Polite))
    println(describe(Any()))
    Criterion("interface-object", greeter, Polite, Idle).report()
}
```

```
started
polite created
hello ada
hello can
empty
polite
other
interface-object: same-object=true, twin=true, other=false -> singleton
```

The `polite created` line showed up after `started` and before the first `greet` call. `Polite` was not built at the start of the program: the declaration was built the moment its name was first accessed, and the `init` block ran at that time. The second access did not print a new line, because the instance already existed. `greeter` and `Polite` pointed to the same object, and the criterion came out `singleton`; even though the type of the `greeter` variable is `Greeter`, the object underneath did not change.

`describe` gave three separate results. `empty` was written for `Idle`, `polite` for `Polite`, and `other` for an ordinary `Any()`. Since an object declaration is a singleton, it matches like a constant inside `when`: the `Idle ->` line comes down to the `Idle == state` comparison. `is Polite` worked too, because a declaration defines both a value and a type; for a type with a single instance, the two give the same result.

The practical conclusion from this measurement has two sides. First, leaving a side effect in the `init` block, that is, printing a line or opening a resource, ties the moment the declaration is built to the flow of the program; which line runs first depends on the place of the first access. Second, when an object declaration implements an interface, every variable typed with that interface still carries the same single instance; the criterion therefore does not differ by type, it stays bound to the object itself.

**TN24.** The first access to an object declaration being made from several threads at the same time was not measured in this lesson; only the order of creation in a single flow was observed.

#### The Reading Rule from the Measurements

Up to this point we measured lines, and every line answered the same question: do two reads give the same object? Putting the lines side by side, a reading rule comes out. A declaration produces a value when its name is written; `Stamp`, `Polite`, `Accepted`, and `Idle` came out `singleton` for this reason. An expression builds a new object every time it is evaluated; `makeMarker()` came out `identity` for this reason. The factory of a class is a function, not a declaration; `Account.open` built a new `Account` on every call, and the result was again `identity`. The rule looks at where the keyword is written: if a name goes to a single value, it is a singleton; if a call or an expression builds a new value, it is identity.

A common mistake is to turn this rule around. A reader who sees the `object` keyword says "this is a singleton"; but the `object : Type { ... }` form is an expression. The opposite is wrong too: when a `companion object` is written inside a class, what the companion object produces is taken to be a singleton. The measurement showed that the companion object itself is one, while what its factory produces is separate. For this reason, state bound to a singleton object, for example a counter or a cache, is shared by all parts of the program; state bound to an object produced by a factory belongs only to the owner of that object.

The moment of creation is a second detail. The `polite created` line showed up on the first access and not on the second. The creation cost or side effect of a singleton object is paid not at the start of the program but at the place where it is first used. This is useful when you need to work out when what you write in an `init` block will run: the order depends on which code accesses it first, and if that code moves, the line moves too. For this reason it is wise to keep the `init` block short, to set up only fields, and not to produce output; in this lesson we printed output only to make the measurement visible.

The third detail is the `when` expression. Matching with `Idle` and the type check with `is Polite` do the same job, because the declaration has a single instance. In a type with a single instance, the questions "is this value equal to that value" and "is this value of that type" give the same answer. In a class where two instances are possible, these two questions would part ways; the absence of this parting is an observable consequence of being a singleton.

#### Predicting Before Measuring

Predicting the criterion before running a line tests whether you really understand the rule. The `Criterion` setup asks three questions: is `===` true when the same object is given, is `==` true when the twin is given, is `==` true when a different object is given? For an object declaration the first question is always answered true, because the name goes to a single value; so the twin must be the same object too, and the third question comes out false only when a different type is given. The criterion is written as `singleton`.

For an object expression, even the first question is answered false. Calling the same function twice gives two separate objects, and the twins are not counted as equal to each other; when both are `false`, the criterion becomes `identity`. You can put every line in this lesson on one of these two paths: if the name points to a declaration, `singleton`; if it points to a call or an expression, `identity`. Even the `data object` line did not step outside this rule; what the `data` prefix added was not equality but a readable text.

The place where a prediction fails is the place where you remembered a rule incompletely. For example, if you knew that `describe` would write `empty` for `Idle` but expected `other` instead of `polite` for `Polite`, it means you thought that the `is` check does not work with the name of a declaration. The measurement corrected this assumption: a declaration is both a value and a type.

#### The Bounding Measurement

An **object expression** is the use of the `object` keyword at the start of an expression: `object { ... }`, or, after a type name, `object : Type { ... }`. It carries no name, and it builds a new object every time the expression is evaluated. The difference between a declaration and an expression is exactly here: the same keyword produces a single object in a declaration and a new object every time in an expression. This is why the measure of this course separates them.

```kotlin
// Main.kt — an object expression builds a new object on every evaluation
object Stamp

fun makeMarker(): Any = object {
    val side = 4
}

fun main() {
    val checks = listOf(
        Criterion("declaration", Stamp, Stamp, makeMarker()),
        Criterion("expression", makeMarker(), makeMarker(), makeMarker()),
    )
    for (check in checks) check.report()
    summarize(checks)
}
```

```
declaration: same-object=true, twin=true, other=false -> singleton
expression: same-object=false, twin=false, other=false -> identity
singleton: 1
identity: 1
content: 0
partial-content: 0
```

The `declaration` line came out `singleton` and the `expression` line came out `identity`. Although the expression has no name and therefore no constructor, every `makeMarker()` call built a new object, and since these objects do not redefine `equals`, they did not come out equal to one another. This measurement is a boundary: it is not enough to assume that something is a singleton when you see the `object` keyword; you need to look at whether the keyword stands in a declaration or in an expression.

**TN25.** How a variable captured by an object expression appears, and the expression extending a type, were not measured in this lesson; only a nameless, typeless expression was used.

#### Summary

- An object declaration builds a type and the single instance of that type together; writing the name is reading the value, and two reads give the same object.
- A companion object is an object declaration inside a class; it is a singleton, but its factory can build the instances of the class separately on every call.
- A `data object` adds only a readable `toString` and equality to the singleton object; it does not change the criterion.
- An object expression builds a new object on every evaluation; the same `object` keyword is a singleton in a declaration and identity in an expression.
- This lesson's criterion: **singleton**. When two reads give the same object, `===` comes out true and the reason for the equality is not sought.

#### Next Step

In this lesson we looked at whether an object is built as one or anew every time, and we kept the behavior only in the body of the class itself. The next lesson separates behavior from a type: interfaces write a contract, and a class can hand it over to another object. Whether handing over also hands over equality will be measured.

### Interfaces and Delegation

Source: https://academia.sh/en/courses/kotlin/interfaces-and-delegation

Measures the interface, the default implementation, and delegation with `by` using the criterion that makes two values equal, and shows that delegation forwards the members of the interface but does not forward `equals`.

In the previous lesson we looked at whether an object is built as a single one or anew every time. In this lesson we take the behavior of the object out of the class: an interface writes a contract, and a class either implements that contract itself or delegates it to another object. The question comes down to delegation: when an object leaves its behavior to someone else, what makes two delegating objects equal?

An **interface** defines the members a type must conform to. In Kotlin an interface can carry not only signatures but also functions with bodies and read-only properties with bodies; these are called default implementations. When a class implements an interface, it has to write the members without bodies, and it overrides the ones with bodies if it wants to.

This lesson recalls the measurement axis once more. The measure from the Java curriculum that asks which type makes the decision (M08/K02) is not here: we look not at the type but at the reason two values count as equal. Being the same object is singleton, equality falling to identity is identity, the contents being equal is content, and only some of them being equal is partial content.

#### The Measurement Setup

`Criterion` stays in the first block, in the same form, in every lesson. Each observation carries three values: `first`, `twin` (a value built separately with the same recipe), and `other` (a value that differs in only one respect). The rule sees the result, not the reason.

```kotlin
// Criterion.kt — classifies the criterion that makes two values equal
class Criterion(val label: String, first: Any?, twin: Any?, other: Any?) {
    val sameObject = first === twin
    val equalTwin = first == twin
    val equalOther = first == other

    val name: String = when {
        sameObject -> "singleton"
        !equalTwin -> "identity"
        !equalOther -> "content"
        else -> "partial-content"
    }

    fun report() {
        println("$label: same-object=$sameObject, twin=$equalTwin, other=$equalOther -> $name")
    }
}

fun summarize(checks: List<Criterion>) {
    for (name in listOf("singleton", "identity", "content", "partial-content")) {
        println("$name: ${checks.count { it.name == name }}")
    }
}
```
The model of the lesson itself stays in a second helper file. The `Printer` interface carries a read-only property and a default function; two classes implement it in different ways.

```kotlin
// Printers.kt — a property and a default implementation in an interface
interface Printer {
    val prefix: String
    val width: Int
        get() = prefix.length

    fun render(text: String): String = prefix + text
}

class PlainPrinter : Printer {
    override val prefix: String = "> "
}

class LoudPrinter : Printer {
    override val prefix: String = "# "
    override fun render(text: String): String = prefix + text.uppercase()
}
```

**TN26.** `Printer` has only one property without a body and one read-only property with a body; a property stored in the interface (one that carries a field) cannot be written, and this was not measured here. The blocks below compile the two `Printer` implementations and the wrappers written with `by` together with this helper.

#### An Interface Member Can Carry a Body

The block below uses the two classes side by side. `PlainPrinter` wrote only the property without a body; `render` and `width` took the bodies in the interface. `LoudPrinter`, on the other hand, redefined `render`. The last line measures two `PlainPrinter` objects.

```kotlin
// Main.kt — interface default and override
fun main() {
    val plain = PlainPrinter()
    val loud = LoudPrinter()
    println(plain.render("ada"))
    println(loud.render("ada"))
    println(plain.width)
    val printers: List<Printer> = listOf(plain, loud)
    println(printers.map { it.render("x") })
    Criterion("two-defaults", PlainPrinter(), PlainPrinter(), loud).report()
}
```

```
> ada
# ADA
2
[> x, # X]
two-defaults: same-object=false, twin=false, other=false -> identity
```

`plain` used the `render` body of the interface and `loud` used its own body; from the `printers` list both were called under the same `Printer` name, and each gave its own result. `width` was defined only in the interface and gave the length of `prefix`. In the last line, two separate `PlainPrinter` objects came out `identity`: because the class does not redefine `equals`, equality fell to identity, and the two instances were not equal even though they use the same interface body. A default implementation shares behavior; it does not share equality.

#### Conflicting Defaults

If a class implements two interfaces and both carry a function with a body and the same signature, the compiler cannot know which one to choose. The class has to redefine the function itself, and inside the body each one can be reached separately with `super<Interface>`.

```kotlin
// Main.kt — the same default of two interfaces combined in a single class
interface Reader {
    fun describe(): String = "reader"
}

interface Writer {
    fun describe(): String = "writer"
}

class Both : Reader, Writer {
    override fun describe(): String = super<Reader>.describe() + "+" + super<Writer>.describe()
}

fun main() {
    println(Both().describe())
    println(Both().let { it as Reader }.describe())
}
```

```
reader+writer
reader+writer
```

`Both` combined the two sources. The second line matters: when the `Both` object was seen as a `Reader`, the same combined body ran, because the choice was made by the function the object itself redefined, not by the type of the variable.

**TN27.** In this block we did not separately verify that a class that does not resolve the conflict fails to compile; only the resolved form was measured.

#### Delegation with by

**Delegation** is leaving the implementation of an interface to another object. In Kotlin this is written in the form `: Printer by inner`: for every member of the interface, the compiler generates a function that forwards the call to `inner`. The object that forwards is called the delegating object, and the object the call goes to is called the **delegate**.

```kotlin
// Main.kt — delegation with by forwards the members of the interface
class Labeled(private val inner: Printer) : Printer by inner

class Quiet(private val inner: Printer) : Printer by inner {
    override fun render(text: String): String = inner.render(text).lowercase()
}

fun main() {
    val loud = LoudPrinter()
    val labeled = Labeled(loud)
    println(labeled.render("ada"))
    println(labeled.prefix)
    println(labeled.width)
    println(Quiet(loud).render("ADA"))
    val checks = listOf(
        Criterion("same-delegate", Labeled(loud), Labeled(loud), Labeled(PlainPrinter())),
        Criterion("overridden", Quiet(loud), Quiet(loud), Quiet(PlainPrinter())),
    )
    for (check in checks) check.report()
    summarize(checks)
}
```

```
# ADA
#
2
# ada
same-delegate: same-object=false, twin=false, other=false -> identity
overridden: same-object=false, twin=false, other=false -> identity
singleton: 0
identity: 2
content: 0
partial-content: 0
```

`Labeled` wrote no members; `render`, `prefix`, and `width` still worked: all three went to `loud`. That the `render` result is in capital letters shows that the call went not to the default body of `Printer` but to the body redefined by `LoudPrinter`. `Quiet`, on the other hand, redefined one member itself and delegated the rest.

Two `Labeled` objects wrapping the same delegate and two `Quiet` objects wrapping the same delegate both came out `identity`. Even though the two delegating objects are bound to the same `loud`, they were not counted as equal.

**TN28.** The expression after `by` does not have to be a constructor parameter; here we measured only the form that shows the parameter. Whether delegation also forwards a member that is not in the interface was not measured.

#### The Functional Interface, the Object Expression, and the Delegate Expression

An interface is not implemented only by classes. An interface with a single abstract function can be built with a lambda if it is marked with `fun interface`. An interface can also be implemented by a nameless object expression. Third, the expression after `by` does not have to be a constructor parameter; it can be any expression. We measure these three in a single block.

`Shout` is built with two lambdas: the bodies of the two are the same. `anon` implements `Printer` with an object expression. The `Cached` class builds its delegate with the `makePrinter()` call, and the `made` counter counts how many times this call ran.

```kotlin
// Main.kt — fun interface, object expression, and the delegate expression evaluated once
fun interface Shout {
    fun apply(text: String): String
}

var made = 0

fun makePrinter(): Printer {
    made += 1
    return PlainPrinter()
}

class Cached : Printer by makePrinter()

fun main() {
    val first = Shout { it.uppercase() }
    val second = Shout { it.uppercase() }
    println(first.apply("ada"))
    val anon = object : Printer {
        override val prefix: String = "~ "
    }
    println(anon.render("x"))
    val one = Cached()
    println(one.render("a"))
    println(one.render("b"))
    println(made)
    Cached()
    println(made)
    val checks = listOf(
        Criterion("functional-interface", first, second, Shout { it }),
        Criterion("object-expression", anon, anon, PlainPrinter()),
    )
    for (check in checks) check.report()
}
```

```
ADA
~ x
> a
> b
1
2
functional-interface: same-object=false, twin=false, other=false -> identity
object-expression: same-object=true, twin=true, other=false -> singleton
```

The first result of the block is that two lambdas with the same body are not counted as equal: the `functional-interface` line came out `identity`. The bodies being the same as text did not produce equality; each lambda expression built its own object, and the functional interface does not redefine `equals`. Implementing the same interface with a class and implementing it with a lambda do not part ways as far as equality goes.

The second result is for the object expression. The `object-expression` line came out `singleton`, because `anon` is a single variable and the same value was given to the measure twice. The distinction we saw in the previous lesson holds here too: an expression builds a new object every time it is evaluated, but an object built once and put into a variable stays single. The `~ ` prefix in the output of `anon.render("x")` showed that the default `render` body in the interface is used in the object expression too.

The third result is for the `by` expression. `made` printed `1` after the first `Cached()` call and `2` after the second. The delegate expression is evaluated once while each `Cached` instance is being built: two instances carry two separate `PlainPrinter` objects. The two `render` calls of the same instance did not rebuild the delegate; `made` increased only in the constructor. This says that delegation is not a method call but a field stored in the constructor: the delegate is built once per instance and then forwarded to.

**TN29.** Whether a lambda capturing or not capturing an outer variable changes equality was not measured; neither lambda captured anything.

#### The Equality Rule from the Measurements

Every line we measured in this lesson asked where a behavior comes from and then asked where equality would come from. An interface shares behavior but carries no state and writes no `equals`; `by` forwards behavior to a delegate but does not forward `equals`; `fun interface` turns a lambda into an interface value but does not count two identical lambdas as equal. The common result of the three setups is that equality is determined not by the sharing of behavior but by the class's own `equals` definition.

A common mistake is to read delegation like inheritance. In inheritance, a subclass inherits the `equals` of the superclass; a class that delegates with `by` forwards the members of the interface, but `equals` is not forwarded, because it is not a member defined in the interface. This is why two `Labeled` or two `Quiet` instances wrapping the same delegate, did not come out equal, even though their behaviors are exactly the same. If equality is needed, the class writes it itself or is declared as a `data class`; delegation does not do this job.

The delegate expression being evaluated once per instance gives a second practical conclusion. The `made` counter printed `2` for two instances; that is, the delegate is a field and stays the same for the lifetime of the instance. If state is kept inside a delegate, for example a counter or a cache, that state belongs to the delegating instance and is not shared between two instances.

The same lesson holds for interface values built with a lambda: even if two bodies carry the same text, they are two separate values, and the result comes out `identity`. Expecting equality from behavior turned out wrong in every setup we measured in this lesson. Thinking of behavior and equality separately keeps you from running into an unexpected result in a collection or in a `==` comparison.

#### Predicting Before Measuring

Because the criterion of this lesson is `identity`, the prediction is easy, but saying why each line gave `identity` is more instructive. In an interface implemented by a class, equality depends on the `equals` of the class; if the class did not write it, the identity comparison of `Object` applies. `by` adds nothing to this table, because the forwarded members are the ones defined in the interface. In a class declared as a data class, `equals` is generated by the compiler and looks at the content; this lesson showed it as a comparison.

There is a prediction trap in the functional interface line. The two lambdas carry the same text and are turned into the same interface; it seems natural to think "they do the same job, so they are equal". The measurement gave `identity`. In the object expression line, on the other hand, the same variable was given twice and the result was `singleton`; a new object is built while the expression is evaluated, but an object put into a variable stays single from then on. The difference between these two lines is the difference between "two expressions" and "one expression, two reads".

In the delegate expression, what is to be predicted is a number. For a program that builds two `Cached` objects, the value of `made` will be `2`, because the delegate is evaluated once in the constructor. A person who expects this number to be `1` or as many as the number of calls has misunderstood whether delegation is a field or an expression recalculated on every call.

#### The Bounding Measurement

Why are two delegating objects not equal? Because `by` forwards only the members of the interface; `equals`, `hashCode`, and `toString` are not members of the interface but members of `Any`. We verified this first: the fragment below did not compile on Kotlin 2.4.21, which is why it stays without output. It is also forbidden for an interface to give a default implementation for the `equals` of `Any`.

```kotlin
interface Same {
    override fun equals(other: Any?): Boolean = true
}
```

Since delegation does not forward `equals`, the only way to change equality is for the class itself to redefine it. A data class does this automatically. The block below measures an ordinary class and a data class wrapping the same delegate side by side; the third line compares the delegating object with its delegate.

```kotlin
// Main.kt — delegation does not forward equals, a data class generates its own
class Wrapped(val inner: Printer) : Printer by inner
data class Tagged(val inner: Printer) : Printer by inner

fun main() {
    val loud = LoudPrinter()
    val checks = listOf(
        Criterion("class-delegation", Wrapped(loud), Wrapped(loud), Wrapped(PlainPrinter())),
        Criterion("data-class-delegation", Tagged(loud), Tagged(loud), Tagged(PlainPrinter())),
        Criterion("delegator-and-delegate", Wrapped(loud), loud, PlainPrinter()),
    )
    for (check in checks) check.report()
    summarize(checks)
}
```

```
class-delegation: same-object=false, twin=false, other=false -> identity
data-class-delegation: same-object=false, twin=true, other=false -> content
delegator-and-delegate: same-object=false, twin=false, other=false -> identity
singleton: 0
identity: 2
content: 1
partial-content: 0
```

The criterion came out `identity` in the ordinary class and `content` in the data class. Because the data class generates its own `equals` and compares the `inner` in the primary constructor, two instances wrapping the same delegate became equal; the one wrapping a different delegate did not. The third line shows the limit of delegation openly: `Wrapped(loud)` and `loud` are not equal to each other. The delegating object behaves like its delegate but is not its delegate.

#### Summary

- An interface writes the members a type must conform to; it can carry functions with bodies and read-only properties with bodies.
- If two interfaces bring the same default function, the class redefines it and reaches the two separately with `super<Interface>`.
- `by` forwards the members of the interface to the delegate; the bodies the delegate redefined itself are preserved.
- Delegation does not forward `equals`, and an interface cannot give a default implementation for `equals`; two ordinary objects wrapping the same delegate are not equal, while two data classes are equal.
- This lesson's criterion: **identity**. Even when behavior is shared, equality falls to identity unless the class redefines its `equals`.

#### Next Step

Delegation moved behavior to another object inside the class. The next lesson adds behavior from outside the class: extension functions let you write a new function for a type whose source you do not touch. We will measure whether an extension can take the place of a member of the type itself, especially over `equals`.

### Extension Functions

Source: https://academia.sh/en/courses/kotlin/extension-functions

Writes an extension function, an extension property, and an extension on a nullable receiver, and measures that an extension cannot shadow a member and is chosen by the static type.

In the previous lesson a class handed its behavior to another object with `by`, and we saw that delegation does not forward `equals`. In this lesson the behavior is added to the class from the outside: you can give a new function to a type whose source you cannot touch. The question is whether this addition can take the place of a member of the type, and in particular whether it can change a member such as `equals`.

An **extension function** is a function written in front of the name of an existing type and called on an instance of that type: `fun String.wrapped(...)`. The definition of the type is not touched; the extension only makes the call syntax look like the type's own. Inside the body, `this` points to the object the function was called on, that is, the receiver.

This `this` should not be confused with the `this` and `it` distinction we saw in the Scope Functions lesson: there the question was which object is visible, while here `this` is only the receiver of the extension and the measurement is separate from that question. The measure of this lesson is the same as everywhere in this course: what makes these two values equal?

#### The Measurement Setup

`Criterion` stays in the first block, in the same form, in every lesson. Each observation carries three values: `first`, `twin` (a value built separately with the same recipe), and `other` (a value that differs in only one respect). The rule sees the result, not the reason.

```kotlin
// Criterion.kt — classifies the criterion that makes two values equal
class Criterion(val label: String, first: Any?, twin: Any?, other: Any?) {
    val sameObject = first === twin
    val equalTwin = first == twin
    val equalOther = first == other

    val name: String = when {
        sameObject -> "singleton"
        !equalTwin -> "identity"
        !equalOther -> "content"
        else -> "partial-content"
    }

    fun report() {
        println("$label: same-object=$sameObject, twin=$equalTwin, other=$equalOther -> $name")
    }
}

fun summarize(checks: List<Criterion>) {
    for (name in listOf("singleton", "identity", "content", "partial-content")) {
        println("$name: ${checks.count { it.name == name }}")
    }
}
```

**TN30.** All the extensions were written in `Main.kt`, next to `main`; an extension defined in a separate file and imported was not measured in this lesson.

#### Extension Function and Property

The block below writes three extensions. `wrapped` adds a mark to both sides of a string. `lastWord` is an extension **property**: its value is not stored, it is computed with `get()` on every read. The third one has `String?` as its receiver; it can be called on a null value too, and inside the body `this` is a nullable type.

```kotlin
// Main.kt — extension function, extension property, and nullable receiver
fun String.wrapped(mark: String): String = mark + this + mark

val String.lastWord: String
    get() = this.substringAfterLast(' ')

fun String?.orPlaceholder(): String = this ?: "none"

fun main() {
    println("ada".wrapped("*"))
    println("one two three".lastWord)

    val present: String? = "here"
    val missing: String? = null
    println(present.orPlaceholder())
    println(missing.orPlaceholder())
}
```

```
*ada*
three
here
none
```

`wrapped` and `lastWord` were written on a `String` as if they were members of the type. `orPlaceholder` did not blow up on the null `missing`: an ordinary member call would not work on null, but because an extension takes the receiver like a parameter, null is a valid receiver too. For this reason an extension with a `String?` receiver can handle null without a safe call (`?.`).

An extension property cannot hold a field: it cannot be given a stored value. We checked this first: the fragment below did not compile in Kotlin 2.4.21, which is why it stays without output.

```kotlin
val String.cache: Int = 0
```

**TN31.** The reason for the rejection, that an extension property does not carry a field, is taken from the definition of the language and not from the error text of the compiler; we do not write the error text in the lesson.

#### An Extension Cannot Shadow a Member

What happens if you write an extension with the same name as a member of a type, but with a narrower parameter type? The block below adds an `equals(other: Plain)` extension to the `Plain` class, next to the `equals(other: Any?)` member that comes from `Any`. The extension compares the `id`s of two `Plain` values. We call it in three ways: a direct call, `==`, and a call through a variable of type `Any`.

```kotlin
// Main.kt — an equals extension does not shadow the member
class Plain(val id: Int)

fun Plain.equals(other: Plain): Boolean = this.id == other.id

fun main() {
    val a = Plain(1)
    val b = Plain(1)
    val asAny: Any = b

    println(a.equals(b))
    println(a == b)
    println(a.equals(asAny))
    Criterion("equals-extension", Plain(1), Plain(1), Plain(2)).report()
}
```

```
false
false
false
equals-extension: same-object=false, twin=false, other=false -> identity
```

All three calls gave the same result: the comparison came out false even though the `id`s are equal. The extension was written, but no call went to it: the member takes `Any?`, so a `Plain` argument fits it too, and when a member fits the call the extension is not considered, even though its parameter is more specific. The extension has a different signature; it does not shadow the member and only stands as an overload. `a == b` always resolves to the `equals(Any?)` member and never calls the extension. This is why the criterion is `identity`: the two `Plain` values carry the same `id`, but the equality rule does not know it.

**TN32.** We do not write the claim that the compiler gives a warning when an extension collides with a member; the compiler warning is not checked in this lesson. Even if an extension is written with a parameter type different from the member, the member is chosen when the argument fits it; the extension gets its turn only when the member cannot be applied. This case is measured in the next section with `Box.scale`.

#### Same Name, Different Parameter

In the previous section we looked at a call whose argument fits both the member and the extension, and saw that the member wins. What if the argument does not fit the member? The member takes an `Int`, the extension takes a `Double`, and the type of the argument decides which one the call goes to. The block below measures this on the `Box` class. It also adds an extension marked `infix` and a `toString` call on a nullable receiver.

```kotlin
// Main.kt — extension with a different parameter is chosen, infix extension, toString on a null receiver
class Box(val size: Int) {
    fun scale(factor: Int): Int = size * factor
}

fun Box.scale(factor: Double): Double = size * factor

infix fun Box.bigger(other: Box): Boolean = size > other.size

fun main() {
    val box = Box(3)
    println(box.scale(2))
    println(box.scale(2.5))
    println(box bigger Box(2))

    val nothing: Any? = null
    println(nothing.toString())
    println(nothing?.toString())
    Criterion("box", Box(1), Box(1), Box(2)).report()
}
```

```
6
7.5
true
null
null
box: same-object=false, twin=false, other=false -> identity
```

`box.scale(2)` went to the member and gave `6`; `box.scale(2.5)` did not fit the `Int` parameter of the member, so it went to the extension and gave `7.5`. The extension lost while the member fitted the call and worked when it covered an argument type the member did not. This draws the limit of "an extension cannot shadow a member": if the member applies, the extension is not considered.

The expression `box bigger Box(2)` gave `true`. The `infix` mark lets you write an extension like a binary operator, without the dot and the parentheses. This is only a convenience of writing.

The lines on the nullable receiver are interesting. The variable `nothing` has the type `Any?` and the value null; `nothing.toString()` printed `null` without blowing up. The reason is that the standard library has a `toString` extension with an `Any?` receiver; it is called as if it were a member, but it handles null. `nothing?.toString()` also printed `null`, but this time because of the safe call: the call was never made and the value of the expression became null. The two lines gave the same output by different routes; in one the function ran, in the other it did not.

The `Criterion` line is again `identity`: `Box` is an ordinary class, and the extensions did not touch equality.

**TN33.** The `Box.scale` member takes an `Int` and the extension takes a `Double`; their signatures differ. It was measured that the call `box.scale(2)` goes to the member; since the extension expects a `Double`, it is not a candidate for an `Int` argument in the first place. The `Int` argument fits the member, so the member was chosen.

#### Generic Receiver and Extension Inside a Class

An extension shows two more odd behaviors. The receiver type can be generic: an extension written on `List<T>` works with every element type. And an extension can be declared inside a class; then it can be called only from inside that class, and it can reach the members of the class too. The block below measures the two together.

```kotlin
// Main.kt — extension with a generic receiver and an extension declared inside a class
fun <T> List<T>.secondOrNull(): T? = if (size >= 2) this[1] else null

class Formatter(private val mark: String) {
    fun String.marked(): String = mark + this + mark

    fun run(text: String): String = text.marked()
}

fun main() {
    println(listOf(1, 2, 3).secondOrNull())
    println(listOf("a").secondOrNull())
    println(emptyList<Int>().secondOrNull())
    println(Formatter("|").run("ada"))
    Criterion("formatter", Formatter("|"), Formatter("|"), Formatter("#")).report()
}
```

```
2
null
null
|ada|
formatter: same-object=false, twin=false, other=false -> identity
```

`secondOrNull` worked on three lists: `2` on the list of three `Int` values, `null` on the one-element `String` list, and `null` on the empty list. Although a single extension was written for different element types, every call inferred its own type; because the return type is `T?`, null was returned safely in the empty case.

The `marked` extension inside `Formatter` read the `mark` property of the class: `Formatter("|").run("ada")` gave `|ada|`. In the extensions above, the receiver was a single `this`; here there are two receivers, the `Formatter` instance and the `String` receiver. `marked` is visible only inside the body of `Formatter`; outside the class, `"ada".marked()` cannot be written.

The criterion is again `identity`: even though two `Formatter` values carry the same mark, they are not equal. That extensions do not touch the equality of the type, whether they are written outside or inside a class, was repeated in the third measurement of this lesson.

**TN34.** That a call to `marked` from outside the class does not compile was not measured separately; the scope information is taken from the definition of the language.

#### The Selection Rule from the Measurements

An extension function is added to a type from the outside, but the way the call is written looks like a member call. This similarity makes the question of which one is chosen necessary. The measurements of this lesson gave three rules. The first: if the argument fits the member, the member wins; the `equals` member takes `Any?`, so the `Plain` argument fitted it, and the `Plain.equals` extension was chosen in none of the three calls, even though its parameter is more specific. The second: if the member cannot be applied, the extension gets its turn; the `Box.scale` member expects an `Int`, so `scale(2.5)` did not fit it and went to the extension. The third: the choice is made by the static type of the variable; the extension does not look at the class at run time.

A common mistake is to take an extension as a way of changing a member. A person who writes an extension with the same name to "fix" a member of a type sees that the code keeps working and nothing changes, because the extension is never called as long as the member fits the call. If the behavior of the member has to change, the type itself changes or a new name is chosen. The same result was seen for `equals` in this measurement, and the criterion stayed `identity`.

The nullable receiver detail shows a second trap. `nothing.toString()` and `nothing?.toString()` printed the same output, but in the first the function ran and in the second it did not. You cannot tell from the output which one was used; the difference shows when the function has a side effect. A person who writes an extension with a nullable receiver must know that this function will also run on a null receiver, and must write its body accordingly.

The generic-receiver extension and the extension inside a class bring up the question of scope. `secondOrNull` was written once and worked on three different lists; `marked` was visible only inside the body of `Formatter`. The first is a function outside the type that can be called from anywhere; the second is a function that reaches the members of the class but can be called only from inside that class. Which one you choose depends on whether the function needs the state of the class: if it does, it is written inside the class, and if it does not, outside.

#### Predicting Before Measuring

Every measurement in this lesson fits the same pattern: write a call, say which one will run, then look at the output. If you can say the member for `box.scale(2)` and the extension for `box.scale(2.5)`, you have understood the selection rule: the member if the argument fits the parameter of the member, the extension if it does not. The same logic holds for `bigger`; since `Box` has no member named `bigger`, the extension runs, and the `infix` mark only changes the way it is written.

On the null receiver, the prediction has two steps. A person who says "it blows up" for `nothing.toString()` has assumed that `toString` is a member and cannot be called on null. The measurement printed `null`, because the call went not to a member but to an extension that handles a null receiver. This means that the way an expression is written (`nothing.toString()`) does not tell which function it calls; you have to look at the type of the receiver and at the visible extensions.

With the generic-receiver extension, the prediction is about the return value. On a list of three elements `2` is expected, and on a one-element or empty list `null`; the `T?` return type covers these two cases with the same value. On the identity lines, `Box` and `Formatter` are ordinary classes, so how many extensions are written does not affect the result.

Equal output and equal route are two separate facts: the two ways of writing `nothing` gave the same output but did not take the same route, and a person who looks only at the output does not see the second. It is a good habit to write next to each line the route by which that output was reached.

#### The Bounding Measurement

If an extension cannot shadow a member, by which rule is an extension chosen? Members are chosen by the run-time class of the object. Extensions are chosen by the declared type of the variable, that is, by the static type. **Static dispatch** means that the function to be called is chosen at compile time by looking at the declared type of the variable; **dynamic dispatch** means that it is chosen by looking at the run-time class of the object. Extensions use the first.

The block below holds the same object with two different static types. One `label` extension is written for each of `Speaker` and `Loud`, and `speak` is a member of the class, which `Loud` overrides. In M08/K02 we looked at which type makes the decision; this measurement does not return to that question, and the lines below only put these two selection rules side by side.

```kotlin
// Main.kt — extension is chosen by the static type, member by the object
open class Speaker {
    open fun speak(): String = "base"
}

class Loud : Speaker() {
    override fun speak(): String = "loud"
}

fun Speaker.label(): String = "label-base"

fun Loud.label(): String = "label-loud"

fun main() {
    val exact = Loud()
    val asSpeaker: Speaker = exact

    println(exact.label())
    println(asSpeaker.label())
    println(exact.speak())
    println(asSpeaker.speak())
    Criterion("two-static-types", asSpeaker, exact, Speaker()).report()
}
```

```
label-loud
label-base
loud
loud
two-static-types: same-object=true, twin=true, other=false -> singleton
```

`asSpeaker` and `exact` hold the same object: the last line printed `singleton`. Even so, `label` gave two different results, because an extension looks at the type of the variable: the `Loud` extension was chosen for `exact` and the `Speaker` extension for `asSpeaker`. `speak` gave the same result on both variables, because a member looks at the real class of the object. Equality is a member too, and for this reason an extension cannot change it: `==` goes to the `equals` of the run-time object, and an extension cannot be chosen.

#### Summary

- An extension function adds a new function to a type without touching its source; in the body, `this` is the receiver.
- An extension property cannot hold a field and is computed on every read; an extension property with a stored value does not compile.
- An extension whose receiver is a nullable type can be called on null and handles null itself.
- If a member fits the call, the member wins, even when the extension's parameter is more specific; since `==` always resolves to the `equals(Any?)` member, an extension cannot change `equals`.
- An extension is chosen by the static type of the variable, a member by the run-time class of the object.
- The criterion of this lesson: **identity**. Even if an extension writes `equals`, the member wins, so equality falls to identity unless the class redefines its own `equals`.

#### Next Step

In this lesson we added a function to the outside of a type and measured that what is added is chosen not by the type but by the type of the variable. The next lesson makes the type itself a parameter: generics let you write a class or a function so that it works with more than one type, and variance tells whether these types can be assigned to each other.

### Generics and Variance

Source: https://academia.sh/en/courses/kotlin/generics-and-variance

Measures a generic class, a generic function, an upper-bound constraint, `out` and `in` variance, and type erasure; shows that the type parameter is erased at run time and that equality looks at the value.

In the previous lesson we saw that an extension is chosen by the static type: the decision was made on the declared type of the variable. In this lesson we put a parameter inside that declared type. `List<Int>` and `List<String>` look like two separate types; the questions are whether the two are really separate at run time, and whether a `List<Int>` can take the place of a `List<Any>`.

**Generics** are the way of writing a class or a function with a **type parameter** whose type is decided later: `class Holder<T>`. The parameter is filled with a concrete type at the call. A **constraint** is a condition that limits the types the type parameter can be filled with: `T : Comparable<T>` accepts only comparable types.

The measure is the same as in every lesson of this course: what makes these two values equal? In M08/K02 we looked at which type makes a decision; here the question is different: we measure whether the type parameter affects equality.

#### The Measurement Setup

`Criterion` stays in the first block, in the same form, in every lesson. Each observation carries three values: `first`, `twin` (a value built separately with the same recipe), and `other` (a value that differs in only one respect). The rule sees the result, not the reason.

```kotlin
// Criterion.kt — classifies the criterion that makes two values equal
class Criterion(val label: String, first: Any?, twin: Any?, other: Any?) {
    val sameObject = first === twin
    val equalTwin = first == twin
    val equalOther = first == other

    val name: String = when {
        sameObject -> "singleton"
        !equalTwin -> "identity"
        !equalOther -> "content"
        else -> "partial-content"
    }

    fun report() {
        println("$label: same-object=$sameObject, twin=$equalTwin, other=$equalOther -> $name")
    }
}

fun summarize(checks: List<Criterion>) {
    for (name in listOf("singleton", "identity", "content", "partial-content")) {
        println("$name: ${checks.count { it.name == name }}")
    }
}
```

**TN35.** Because the parameters of `Criterion` are `Any?` in this lesson, values with type parameters also look like ordinary objects when they are passed to it; the measurement sees the run-time object, not the static type.

#### Generic Class, Function, and Constraint

The block below writes three generic definitions. `Holder<T>` holds a value of any type. `largest` works only on comparable types, with the `T : Comparable<T>` constraint. `sumInts` asks for two constraints together: the `where` clause is the way to put more than one constraint on a single parameter.

```kotlin
// Main.kt — generic class, upper-bound constraint, and where
class Holder<T>(val item: T) {
    fun describe(): String = "box:" + item
}

fun <T : Comparable<T>> largest(first: T, second: T): T =
    if (first >= second) first else second

fun <T> sumInts(first: T, second: T): Int where T : Number, T : Comparable<T> =
    first.toInt() + second.toInt()

fun main() {
    println(Holder(7).describe())
    println(Holder("ada").describe())
    println(largest(3, 9))
    println(largest("apple", "pear"))
    println(sumInts(2, 3))
}
```

```
box:7
box:ada
9
pear
5
```

The same `Holder` was used with an `Int` and with a `String`; the type parameter was inferred at each call, and no type was added to the text. `largest` worked with both numbers and strings, because both are `Comparable`. `sumInts` compiles only for types that are both `Number` and `Comparable`; `Int` satisfies both.

**TN36.** That passing a type that does not satisfy the constraint to `largest` fails to compile was not measured separately; only calls that satisfy the constraint were written.

#### No Type Parameter at Run Time

The type parameter of a generic class is checked at compile time and is not carried to run time. This is called **type erasure**. The result is that two values of the same generic type built with different parameters are of the same class at run time. More striking, some standard functions return a single shared object: an empty list can be the same object for every element type. We measure this.

We also put three small tools side by side. A **star projection**, written as `List<*>`, allows reading without knowing the type parameter. A parameter marked `reified` in an `inline` function becomes readable at run time; this is the only exception to erasure, because the compiler expands the body of that function at the call site.

```kotlin
// Main.kt — type erasure and reified
class Holder<T>(val item: T)

fun countAll(values: List<*>): Int = values.size

inline fun <reified T> nameOf(): String = T::class.simpleName ?: "unnamed"

fun main() {
    val ints = Holder(1)
    val texts = Holder("ada")
    println(ints::class == texts::class)
    println(countAll(listOf(1, 2, 3)))
    println(countAll(listOf("a", "b")))
    println(nameOf<Int>())
    println(nameOf<String>())

    val checks = listOf(
        Criterion("empty-lists", emptyList<Int>(), emptyList<String>(), listOf(1)),
        Criterion("boxes", ints, texts, Holder(2)),
    )
    for (check in checks) check.report()
    summarize(checks)
}
```

```
true
3
2
Int
String
empty-lists: same-object=true, twin=true, other=false -> singleton
boxes: same-object=false, twin=false, other=false -> identity
singleton: 1
identity: 1
content: 0
partial-content: 0
```

`ints` and `texts` were built with different type parameters, but their classes came out the same. `countAll` read the sizes of the two lists without knowing their elements. `nameOf` showed the limit of erasure: with `reified` the type parameter could be read at run time, and the names `Int` and `String` were printed.

The first line of the criterion table is `singleton`: `emptyList<Int>()` and `emptyList<String>()` resolved to the same object. Although they were called with two separate type parameters, there was a single empty list in hand, because the element type is not stored in the object. The second line is `identity`: `Holder` is an ordinary class that does not redefine `equals`. The type parameter did not change equality in either direction.

#### Variance: out and in

Even if the type parameter is erased inside the object, the compiler checks it in assignments. **Variance** tells whether a `Box<Sub>` value can take the place of a `Box<Super>`. It has three forms. **Covariance** means a box of a subtype can take the place of a box of a supertype, and it is marked with `out`: the parameter can be used only in an output position, that is, in a return type. **Contravariance** is the opposite and is marked with `in`: the parameter can be used only in an input position, that is, as a function parameter. **Invariance** is the state in which neither is allowed, and it is the default of an unmarked type parameter.

```kotlin
// Main.kt — assignment with out and in
class Source<out T>(private val value: T) {
    fun get(): T = value
}

class Sink<in T> {
    fun accept(item: T): String = "accepted:" + item
}

fun main() {
    val ints: List<Int> = listOf(1, 2)
    val anys: List<Any> = ints
    println(anys.size)

    val source: Source<Any> = Source<String>("ada")
    println(source.get())

    val sink: Sink<String> = Sink<Any>()
    println(sink.accept("ali"))
    Criterion("covariant-assignment", ints, anys, listOf<Any>("x")).report()
}
```

```
2
ada
accepted:ali
covariant-assignment: same-object=true, twin=true, other=false -> singleton
```

A `List<Int>` was assigned to a `List<Any>` variable, because `List` is read-only and declared with `out T`; no new object was built in that assignment, the same object was read with two different types, and the criterion came out `singleton`. A `Source<String>` also took the place of a `Source<Any>`. `Sink` worked in the opposite direction: a `Sink<Any>` took the place of a `Sink<String>` that accepts `String`, because a receiver that accepts every `Any` accepts a `String` as well.

A mutable list, on the other hand, cannot be covariant. We checked this first: the fragment below did not compile in Kotlin 2.4.21, which is why it stays without output.

```kotlin
val numbers: MutableList<Int> = mutableListOf(1)
val anys: MutableList<Any> = numbers
```

**TN37.** The reason for the rejection is that if a `String` could be added to a `MutableList<Any>`, the `MutableList<Int>` would be corrupted; this reasoning comes from the definition of the language and was not measured here with a separate block. Only the fact that the assignment does not compile was verified.

#### The Limits of Erasure

Erasure means that the type parameter cannot be read at run time. It has three consequences. First, you can ask whether a value is a `List<*>` but not whether it is a `List<String>`; second, an unchecked cast to `List<String>` can be made and the error appears only when an element is read; third, variance can be marked not only in the class declaration but also at the use site. The block below measures the three.

`numbers` is in fact a list that contains `Int` values, but it is held with the type `Any`. `texts` is an unchecked view of that list as a `List<String>`. `sink` is declared as `MutableList<in Int>`: the type parameter has been made contravariant at the use site.

```kotlin
// Main.kt — is List<*>, unchecked cast, and use-site variance
fun main() {
    val numbers: Any = listOf(1, 2, 3)
    println(numbers is List<*>)
    @Suppress("UNCHECKED_CAST")
    val texts = numbers as List<String>
    println(texts.size)
    val outcome = try {
        val first: String = texts[0]
        "read " + first
    } catch (e: ClassCastException) {
        "class-error"
    }
    println(outcome)
    val sink: MutableList<in Int> = mutableListOf<Any>("x")
    sink.add(5)
    println(sink.size)
    println(sink[1])
    Criterion("unknown-list", numbers, texts, listOf("a")).report()
}
```

```
true
3
class-error
2
5
unknown-list: same-object=true, twin=true, other=false -> singleton
```

`numbers is List<*>` came out true: a star projection is a question asked without knowing the type parameter. `texts.size` printed `3`. Because the cast is unchecked, reading the size did not blow up, since it does not touch the elements. The error came when the first element was read as a `String`: the `class-error` line shows that the `ClassCastException` was caught. The compiler believed that `texts` was a `List<String>`, while the run time saw that the element was really an `Int` only at the moment of reading.

This is the price of erasure. Since the type parameter is not in the object at run time, the correctness of the cast cannot be checked there; the check is postponed until an element is read. The criterion line is `singleton`: `numbers` and `texts` are the same object. The cast did not build a new list, it gave the same object a new type label.

`sink` showed use-site variance. `MutableList<in Int>` is a view into which an `Int` can be added but which yields only `Any?` when read. After `sink.add(5)` the size became `2` and `sink[1]` printed `5`; the first element had stayed in the list as `"x"`. It is the same rule as `out`/`in` in the class declaration, made valid for a single variable.

**TN38.** We do not write the claim that the compiler gives a warning for an unchecked cast; the warning is not checked in this lesson. We wrote the `@Suppress` mark only to show that we accept the cast explicitly.

#### The Erasure Rule from the Measurements

The measurements of this lesson rest on a single fact: the type parameter is not in the object at run time. Three consequences of it were measured. Two empty lists came out as the same object even though their type parameters differ. The question `is List<*>` was answered, but the question `List<String>` could not be asked. And the unchecked cast did not blow up at once: `texts.size` printed `3`, and the error appeared only when an element was read.

A common mistake is to assume that a cast is safe. A person who writes `as List<String>` sees that the compiler now counts the list as a `List<String>`, and everything in the following code looks fine. Yet the content of the object did not change; only the promise given to the code that reads it did. When the promise is broken, the error appears not on the line where the cast was made but on the line where the element was read. If these two lines are far from each other, finding the source of the error gets hard. For this reason, instead of casting a list that comes from outside, it is sturdier to build a new list by checking the elements one by one; this lesson did not measure that, it only showed the limit of the cast.

Variance can be marked in two separate places. If `out` or `in` is written in the class declaration, the rule holds in all uses of the class; if `MutableList<in Int>` is written at the use site, the rule holds only for that variable. The cost of the latter is in the reading direction: `sink[1]` printed `5`, but since the type of the read value is `Any?`, you must first check its type to do anything with it.

The role of the constraint and of `reified` is also read together with erasure. A constraint says at compile time what the type parameter has to look like; it checks nothing at run time. `reified` gets past erasure only in inline functions, because the compiler writes the type information into the call site. These two tools narrow the limits of erasure, but they do not remove it.

#### Predicting Before Measuring

In erasure, prediction comes down to the question "what information exists at run time". An object has a class; it has no type parameter. For this reason `numbers is List<*>` is expected to be true, `texts.size` to be `3`, and a class error to appear when the first element is read as a `String`. All three of the results match the measurement. The prediction that does not match is "the cast line blows up": there is no check that looks at the object during the cast.

In use-site variance, the prediction splits by direction. In the writing direction, `MutableList<in Int>` accepts an `Int`; in the reading direction, the type of the element is `Any?`. To say that the size will be `2`, it is enough to know that the list had one element at the start and one element was added. The `5` that was read depends on knowing the position of the added value.

For the criterion line the prediction is simple. `numbers` and `texts` look at the same list, and the cast does not build a new object; so `same-object` is true and the criterion stays `singleton`. What makes these lines singleton is that the type information is not in the object: the information belongs not to the object but to the compiler that reads the code.

None of these predictions is a habit; all of them derive from a single sentence. The run time knows the class of the object, and it does not know the type parameter. If the result of an expression cannot be derived from this sentence, the expression is asking about something resolved at compile time, for example the validity of an assignment. Separating the compile-time question from the run-time question prevents most of the mistakes made with generics.

#### The Bounding Measurement

Since the type parameter is erased at run time, what makes two values of a generic type equal cannot be the type parameter: it has to be the element values. The block below measures this in two ways. A generic data class, `Wrapper`, is given `1` and `1L`, that is, an `Int` and a `Long`. The second line compares two lists of type `Any`.

```kotlin
// Main.kt — equality in a generic data class looks at the value
data class Wrapper<T>(val value: T)

fun main() {
    val checks = listOf(
        Criterion("wrapper", Wrapper(1), Wrapper(1), Wrapper(1L)),
        Criterion("list-1-1L", listOf<Any>(1), listOf<Any>(1), listOf<Any>(1L)),
        Criterion("empty-and-full", emptyList<Int>(), emptyList<Int>(), listOf(1)),
    )
    for (check in checks) check.report()
    summarize(checks)
}
```

```
wrapper: same-object=false, twin=true, other=false -> content
list-1-1L: same-object=false, twin=true, other=false -> content
empty-and-full: same-object=true, twin=true, other=false -> singleton
singleton: 1
identity: 0
content: 2
partial-content: 0
```

The first two lines came out as `content`: two values built separately with the same recipe were equal, and the equality broke when `1L` was put in place of `1`. `Wrapper(1)` and `Wrapper(1L)` are not equal, because their `value` fields are an `Int` value and a `Long` value and `equals` looks at them; this comparison rests not on the type parameter but on the run-time values. The third line is `singleton`: the same empty list object.

#### Summary

- Generics let you write a class or a function with a type parameter; `T : Comparable<T>` is a constraint, and `where` is the way to put more than one constraint.
- The type parameter is erased at run time; `emptyList<Int>()` and `emptyList<String>()` are the same object, and `reified` gets past erasure in a function.
- `out` gives covariance and `in` gives contravariance; an unmarked parameter is invariant, and a `MutableList<Int>` cannot be assigned to a `MutableList<Any>` variable.
- A read-only `List<Int>` is assigned to a `List<Any>`; no new object is created in the assignment.
- In a generic data class, equality looks not at the type parameter but at the run-time values of the fields.
- The criterion of this lesson: **singleton**. Because the type parameter is erased, two empty lists called with different parameters are the same object.

#### Next Step

With this lesson the topic of types closes: from the single object to the delegated behavior, the function added from the outside, and the erased type parameter, we saw that equality always comes from the value itself. The next topic is "Collections and Concurrency Basics", which begins with "Collection Types": lists, sets, and maps are generic types too, and whether the values inside them count as equal depends not on the kind of the collection but on its content.

## Collections and Concurrency Basics

Read-only and mutable collections, transformation operations with mapping, filtering and reduction, sequences, coroutines, flows, and the unchecked exception model.

### Collection Types

Source: https://academia.sh/en/courses/kotlin/collection-types

Measures that a read-only interface does not mean immutability, that a list's equality comes from its content, and that sets and maps compare equal regardless of order.

The previous lesson, "Generics and Variance", showed that a type parameter is erased at run time. This lesson goes through the structures that carry type parameters most often: collections. A program almost always works on several values at once: order lines, user names, setting keys. Kotlin offers three basic collections for them: the ordered **list (`List`)**, which allows duplicates; the **set (`Set`)**, which does not; and the **map (`Map`)**, which goes from a key to a value.

Each of the three has two faces. The `List`, `Set` and `Map` interfaces declare only read operations: the element count, access to an element, the containment question. The operations that add and remove elements live in `MutableList`, `MutableSet` and `MutableMap`. The language has two names for this split, and they must not be confused. A collection is **read-only** when the interface we hold offers no write operation. A collection is **immutable** when the object itself cannot change by any route. The first is a property of an interface, the second a property of an object. The lesson measures this difference, then asks what decides whether two collections count as equal.

The shared question of this topic is: **what makes these two values equal?** There are four criteria. If the two values that come out equal are the same object, the criterion is **singleton**. If the twin is a separate object and is not equal, it is **identity**. If the twin is equal and the value that differs in one respect is not equal, it is **content**. If the different value comes out equal as well, it is **partial content**. Every measurement builds three values: `first`, its twin `twin` built separately by the same recipe, and `other`, which differs in one respect only. The `Criterion` class below compares the three with `===` and `==`, names the criterion and prints it as a line. The class is defined once and every measurement uses it; the rule sees the result, not the reason.

```kotlin
// Criterion.kt — classifies the criterion that makes two values equal
class Criterion(val label: String, first: Any?, twin: Any?, other: Any?) {
    val sameObject = first === twin
    val equalTwin = first == twin
    val equalOther = first == other

    val name: String = when {
        sameObject -> "singleton"
        !equalTwin -> "identity"
        !equalOther -> "content"
        else -> "partial-content"
    }

    fun report() {
        println("$label: same-object=$sameObject, twin=$equalTwin, other=$equalOther -> $name")
    }
}

fun summarize(checks: List<Criterion>) {
    for (name in listOf("singleton", "identity", "content", "partial-content")) {
        println("$name: ${checks.count { it.name == name }}")
    }
}
```

This block has no output: it only defines the measuring tool and is compiled together with every block that follows.

#### A Read-Only Interface, the Same Object

The line `val view: List<Int> = editable` does not build a new list. The type on the left only says which operations are visible; the object on the right is the mutable list built with `mutableListOf`. So although no element can be added through `view`, an element added through `editable` shows up in `view`. For a real copy, `toList()` is used: the call builds a new list from the elements of that moment, and later changes do not reach it. The block below reads this from three questions: what does `view` show after the addition, what does `snapshot` show, and are the two the same object as `editable`?

```kotlin
// Main.kt — a read-only interface is a view of the same object
fun main() {
    val editable = mutableListOf(1, 2)
    val view: List<Int> = editable
    val snapshot = editable.toList()
    editable.add(3)
    println(view)
    println(snapshot)
    println(view === editable)
    println(snapshot === editable)
    println((view as MutableList<Int>).size)
}
```

```
[1, 2, 3]
[1, 2]
true
false
3
```

The first line shows that the addition is visible in `view`, and the second that `snapshot` stayed at the two elements from before the addition. The third line says that `view` is the same object as `editable`, and the fourth that the copy is a separate object. The last line gives a more uncomfortable result: `view`, whose type is read-only, can be cast to `MutableList`, and its size reads as `3`. So **a read-only type is not a guarantee of immutability**; it only states that no write can be made through that reference. If a function returns a `List` while handing out a mutable list internally as is, the caller may hold what it takes for its own list while the content shifts under it. The safe way is to hand out a copy with `toList()` when crossing the boundary.

**CC1.** This measurement is for a list built with `mutableListOf`. Casting a list built with `listOf(...)` to `MutableList` and trying to change it, and the behavior of the other collection builders in this respect, were not measured separately.

#### The Criterion: Content

The question is what makes two lists equal. The first answer that comes to mind is the type: if one is a `MutableList` and the other a `List`, they differ. The measurement shows the opposite. The block below first compares a mutable and a read-only list with the same elements, then one built with `listOf` and one built with `arrayListOf`, using `==`. Then it classifies the same measurement with `Criterion`: `first` and `twin` are built separately by the same recipe (`mutableListOf(1, 2)`), and `other` is a list that differs only in its last element.

```kotlin
// Main.kt — lists compare by content, not by type
fun main() {
    println(mutableListOf(1, 2) == listOf(1, 2))
    println(listOf(1, 2) == arrayListOf(1, 2))
    val check = Criterion("list", mutableListOf(1, 2), mutableListOf(1, 2), listOf(1, 3))
    check.report()
}
```

```
true
true
list: same-object=false, twin=true, other=false -> content
```

The first two lines show that lists of different types and implementations come out equal with the same elements. The third line names the criterion: it is not `singleton`, because the twin is a separate object; not `identity`, because the twin is equal; not `partial content`, because the different element does not come out equal. The remaining criterion is **content**. This is the criterion of the lesson: two lists are equal if they carry the same elements in the same order; which interface they are seen through or which class they come from does not change the result. This follows from structural equality (`==`) carrying the `equals` call down to the elements in collections. Referential equality (`===`), on the other hand, always comes out false for two separate lists and decides nothing here.

**CC2.** This measurement is for `Int` lists with two elements. The equality of lists whose elements come from classes that do not define their own `equals` was not measured separately; that case is the subject of the next lesson.

#### The Bounding Measurement

The content criterion also includes the order information in a list: `listOf(1, 2)` and `listOf(2, 1)` are not equal even though they hold the same elements. If we ask the same question of a set and a map, we find the limit of the criterion. The block below prints five measurements at once. In the list, `other` is in reverse order. In the set and the map, `other` is also a collection that differs only in order; in the `map-value` measurement, though, `other` differs in one value. In the last measurement the type parameters of the two empty lists are deliberately kept different: `emptyList<Int>()` and `emptyList<String>()`.

```kotlin
// Main.kt — order is not a difference in sets and maps
fun main() {
    val checks = listOf(
        Criterion("list", listOf(1, 2), listOf(1, 2), listOf(2, 1)),
        Criterion("set", setOf(1, 2), setOf(1, 2), setOf(2, 1)),
        Criterion("map", mapOf("a" to 1, "b" to 2), mapOf("a" to 1, "b" to 2), mapOf("b" to 2, "a" to 1)),
        Criterion("map-value", mapOf("a" to 1, "b" to 2), mapOf("a" to 1, "b" to 2), mapOf("a" to 1, "b" to 3)),
        Criterion("empty-list", emptyList<Int>(), emptyList<Int>(), emptyList<String>()),
    )
    for (check in checks) check.report()
    summarize(checks)
}
```

```
list: same-object=false, twin=true, other=false -> content
set: same-object=false, twin=true, other=true -> partial-content
map: same-object=false, twin=true, other=true -> partial-content
map-value: same-object=false, twin=true, other=false -> content
empty-list: same-object=true, twin=true, other=true -> singleton
singleton: 1
identity: 0
content: 2
partial-content: 2
```

The bounding measurement is this: **order is not a difference in sets and maps.** When the order changes in a list, equality breaks (`content`); the same change makes no difference in a set or a map (`partial-content`): the different value comes out equal as well. Set equality looks at the same group of elements, and order is not part of it; a map is compared by its key set and the value of each key, and the order in which the keys were written is outside that. The `map-value` line confirms the second point: when the order stays the same and one value changes, the map does not come out equal. The distribution of the four measurements splits 2/2 between two criteria; the fifth measurement shows something else.

The result on the fifth line stands out in two ways. `emptyList<Int>()` and `emptyList<String>()` are the same object, so the criterion is `singleton` and no comparison is needed at all. Moreover, because this empty list is a single shared object, two calls with different type parameters both return it; since the type parameter is erased at run time, `other` comes out equal to `first`. The type erasure of the previous lesson shows up here as a detail of collection equality.

**CC3.** These measurements are for two-element collections and `emptyList`. The behavior when hash codes collide in larger sets, and sets whose elements do not define their own `hashCode`, were not measured separately.

#### Map and Set Operations

A map is used even more often than a list in daily work, but its read rule surprises in two places. Reading a missing key with `[]` returns `null`, so the result type is a nullable type (`Int?`). `getOrDefault` returns the value you give in the same situation. `getValue`, on the other hand, throws an exception (`NoSuchElementException`) for a missing key. The block below measures these three reads, the write operations and building a new map with the `+` operator together. `stock + ("banana" to 2)` does not change the existing map; it returns a separate map that contains the new key. The last two lines show a set built with `hashSetOf`: the iteration order of this set is not a guarantee, so the elements are printed sorted.

```kotlin
// Main.kt — map reads
fun main() {
    val stock = mutableMapOf("apple" to 3, "pear" to 5)
    println(stock["banana"])
    println(stock.getOrDefault("banana", 0))
    stock["apple"] = 4
    stock.put("cherry", 1)
    println(stock.keys)
    println(stock.values.sum())
    val copy = stock + ("banana" to 2)
    println(stock.size)
    println(copy.size)
    try {
        stock.getValue("banana")
    } catch (e: NoSuchElementException) {
        println(e::class.simpleName)
    }
    val tags = hashSetOf("b", "a", "b")
    println(tags.size)
    println(tags.sorted())
}
```

```
null
0
[apple, pear, cherry]
10
3
4
NoSuchElementException
2
[a, b]
```

Reading it in order, the missing key returned `null`, and the given default returned `0`. `stock["apple"] = 4` changed the value of an existing key, and `put` added a new key; the keys stayed in the order `apple`, `pear`, `cherry`, and the values added up to `10` as `4 + 5 + 1`. The `copy` built with `+` carries four entries while `stock` stayed at three: the new map is separate. `getValue` threw the exception for the missing key and it was caught. The last two lines show that although `"b"` was added twice, the set has two elements and its sorted form is `[a, b]`.

The practical rule that follows: for a key that may be missing, `[]` is used and the result is tied to a default with `?:` (the Elvis operator); for a key that should not be missing, `getValue` is used and the absence surfaces as an error. The choice between the two is also the contract of the map: who decides that the value read exists, the program or the data?

**CC4.** These measurements are for a map built with `mutableMapOf` and a set built with `hashSetOf`. The iteration order of the map built with `mutableMapOf` coincided with insertion order in this measurement; this was not measured as a language guarantee, and no order was claimed for `hashMapOf`.

#### Changing While Iterating

One of the most common collection mistakes is removing an element from a list while iterating over the same list. The `for (x in list)` loop uses an iterator behind the scenes, and the iterator refuses to keep working when it notices that the collection changed by a route other than itself. The block below tries to remove the even numbers inside the loop in a four-element list; it prints the name of the thrown exception and the state of the list at that moment. Then the same job is done with `removeAll`. The last two lines show that a list and a set are not counted as equal even with the same elements, and that a set built from a list comes out equal regardless of order.

```kotlin
// Main.kt — removing while iterating
fun main() {
    val numbers = mutableListOf(1, 2, 3, 4)
    try {
        for (n in numbers) {
            if (n % 2 == 0) numbers.remove(n)
        }
    } catch (e: ConcurrentModificationException) {
        println(e::class.simpleName)
    }
    println(numbers)
    numbers.removeAll { it % 2 == 0 }
    println(numbers)
    println(listOf(1, 2) == setOf(1, 2))
    println(listOf(2, 1).toSet() == setOf(1, 2))
}
```

```
ConcurrentModificationException
[1, 3, 4]
[1, 3]
false
true
```

`2` was removed in the loop's second step and the list changed; the exception was thrown at the next `next()` call. That is why the list stayed in the state `[1, 3, 4]`; `4` was never checked. An even number still remaining in the list shows that the error is not only the exception but also a half-finished cleanup. `removeAll`, on the other hand, applies the condition to all the elements in a single pass and leaves `[1, 3]`. The rule is this: if a collection has to be cleaned by removing, the collection's own bulk operation such as `removeAll` or `retainAll` is used; no removal is made from the collection itself during iteration (the iterator's own `remove` method is the exception).

The last two lines show that equality also depends on the type. A `List` and a `Set` with the same elements are not equal: list equality is defined only against another list, and set equality only against another set. So the list is converted to a set before the comparison; the converted `toSet()` comes out equal regardless of order.

**CC5.** This measurement is for the `mutableListOf` list backed by `ArrayList`. Whether the exception is thrown when the removal is made at the second-to-last element of the list was not measured and is not written as a guarantee.

#### The Cost of a Changing Element

A set and a map place an element or a key by a number (the hash code). If the element is a mutable list and it is changed after it enters the set, the number changes but the set does not update the element's place. The result is an element that sits in the set but cannot be found when searched for. The block below adds a list to a set, adds an element to the list and searches the set in two ways: with the changed list itself and with a newly built list of the same content. The size is read in every case.

```kotlin
// Main.kt — an element that changes after entering a set
fun main() {
    val key = mutableListOf(1)
    val bag = hashSetOf(key)
    println(bag.contains(key))
    key.add(2)
    println(bag.size)
    println(bag.contains(key))
    println(bag.contains(mutableListOf(1, 2)))
    println(bag.any { it === key })
}
```

```
true
1
false
false
true
```

Before the addition the search succeeds. After the addition the set size stays `1`, so the element was not removed; but the search fails both with the changed list and with a new list of the same content. The last line proves the element is still there: iterating over the set directly finds the same object. The element stays in the set, but the set can no longer reach it. This error is silent and throws no exception.

The lesson is plain: values that will be set elements or map keys are chosen from types that do not change after they enter. `String`, numbers and data classes whose fields are all `val` and are themselves immutable are safe in this respect; `MutableList` is not. If a changing value is to become a key, a copy is taken first with `toList()` and the copy becomes the key.

**CC6.** This measurement is for an `ArrayList` element inside a `HashSet`. The behavior of `LinkedHashSet` and tree-based sets in the same situation, and a changing element used as a map key, were not measured.

#### Summary

- The read-only `List`, `Set` and `Map` interfaces give no immutability guarantee; if a `MutableList` that points to the same object changes, the read-only view changes too.
- `toList()` is used for a real copy; a function that crosses a boundary should not return a mutable collection as is, typed as `List`.
- List equality looks at content and order, not at type. The criterion of this lesson: **content**.
- The bounding measurement: order is not a difference in sets and maps (`partial-content`); the empty list is a singleton object and the type parameter does not affect equality.
- A missing key in a map gives `null` with `[]` and an exception with `getValue`.

#### Next Step

This lesson measured the collection as a container of data. The next lesson moves on to the operations that produce new collections from the content of that container: `map`, `filter`, `fold`, `groupBy`. The same question comes back there too: if the same transformation is applied twice to the same list, what are the results equal by?

### Transformation Operations

Source: https://academia.sh/en/courses/kotlin/transformation-operations

Measures that mapping, filtering, folding and grouping produce new collections, that applying the same transformation twice gives equal but separate results, and that distinct and toSet rest on the element's own equality.

The previous lesson measured collections as a container of data and showed that content decides equality. A collection is rarely used as it is. You need to add up the amounts in an order list, separate the orders that belong to one city, pair two lists. All of these are **transformation operations**: they take a collection and produce a new collection or a single value from it. Most transformation operations are **higher-order functions**, that is, they take a lambda as a parameter; some, such as `sorted()` or `distinct()`, take no lambda. The lambda carries the rule to apply to each element.

The four basic operations are these. `map` is **mapping**: it produces a new element from each element, and the result is a list of the same length. `filter` is **filtering**: it keeps the elements for which the lambda says true. `fold` is **folding**: starting from an initial value, it combines the elements into a single value. `reduce` is **reducing**: the form of folding that takes no initial value and counts the first element as the start. The shared measuring question of the course returns in this lesson too: **what makes these two values equal?** The four criteria stay as in the previous lesson, and the `Criterion` class is given again unchanged.

```kotlin
// Criterion.kt — classifies the criterion that makes two values equal
class Criterion(val label: String, first: Any?, twin: Any?, other: Any?) {
    val sameObject = first === twin
    val equalTwin = first == twin
    val equalOther = first == other

    val name: String = when {
        sameObject -> "singleton"
        !equalTwin -> "identity"
        !equalOther -> "content"
        else -> "partial-content"
    }

    fun report() {
        println("$label: same-object=$sameObject, twin=$equalTwin, other=$equalOther -> $name")
    }
}

fun summarize(checks: List<Criterion>) {
    for (name in listOf("singleton", "identity", "content", "partial-content")) {
        println("$name: ${checks.count { it.name == name }}")
    }
}
```

The measurements of this lesson use four data types. `Order` is an order line: an id, a city and an amount. `Ticket` is a **data class** that carries only an id; `Plain` carries the same field but is an ordinary class, not a data class. `Session` is a data class that carries its id in the constructor and its visit count in the body. A data class generates `equals` from the constructor properties; a property declared in the body does not enter that generation. The `session` function is a small helper that sets both fields together. This block has no output either.

```kotlin
// Models.kt — the types the transformation measurements use
data class Order(val id: Int, val city: String, val amount: Int)

data class Ticket(val id: Int)

class Plain(val id: Int)

data class Session(val id: Int) {
    var visits = 0
}

fun session(id: Int, visits: Int): Session {
    val result = Session(id)
    result.visits = visits
    return result
}
```

#### The Four Basic Operations

The first measurement applies the four operations to an `Int` list. `map { it * 2 }` multiplies each amount by two, and `filter { it > 6 }` drops those that are six or below. `fold(0)` gives the sum; because the initial value is `0`, the result type is the type of the initial value. The largest element is found with `reduce`. The last two measurements show where the two operations part on an empty collection: `fold` returns the initial value, while `reduce` is expected to throw an exception because it has no first element to start from.

```kotlin
// Main.kt — mapping, filtering, folding, reducing
fun main() {
    val amounts = listOf(5, 12, 7, 20)
    println(amounts.map { it * 2 })
    println(amounts.filter { it > 6 })
    println(amounts.fold(0) { acc, n -> acc + n })
    println(amounts.reduce { acc, n -> maxOf(acc, n) })
    println(emptyList<Int>().fold(0) { acc, n -> acc + n })
    try {
        emptyList<Int>().reduce { acc, n -> acc + n }
    } catch (e: UnsupportedOperationException) {
        println(e::class.simpleName)
    }
}
```

```
[10, 24, 14, 40]
[12, 7, 20]
44
20
0
UnsupportedOperationException
```

In the first line four elements were mapped to four elements. In the second line only three elements passed the filter: `5` was dropped. The third line is the sum `5 + 12 + 7 + 20`; the fourth line finds `20` with `maxOf`. The fifth line shows that `fold` returns its initial value `0` on an empty list, and the sixth that `reduce` throws `UnsupportedOperationException` in the same situation. The rule is clear: if it is not known whether there are elements, `fold` is chosen; if an empty collection should count as an error, `reduce` is chosen.

**CC7.** These measurements are for `Int` lists. How the result type of a `reduce` over elements of another type narrows to the common supertype of the elements, and sibling operations such as `reduceOrNull`, were not measured separately.

#### Grouping, Building a Map and Pairing

Three more operations produce a collection from a collection, but the result does not have to be a list. `groupBy` collects each element by a key and builds a map from the key to a list. `associate` produces a key-value pair from each element and builds a map from them; `associateBy` takes the key from the element and stores the element itself as the value. If two elements produce the same key, **the later one replaces the earlier**. `zip` pairs two lists in order, and the number of pairs is limited by the shorter list. In the measurement the id of the order under the `ankara` key of the `associateBy` result is read; this shows which of the two ankara orders remained.

```kotlin
// Main.kt — grouping, building a map, zip
fun main() {
    val orders = listOf(Order(1, "ankara", 30), Order(2, "izmir", 10), Order(3, "ankara", 20))
    println(orders.groupBy { it.city }.mapValues { entry -> entry.value.map { it.id } })
    println(orders.associate { it.id to it.amount })
    println(orders.associateBy { it.city }["ankara"]?.id)
    println(orders.map { it.id }.zip(listOf("a", "b", "c")))
    println(listOf(1, 2, 3).zip(listOf("a", "b")))
}
```

```
{ankara=[1, 3], izmir=[2]}
{1=30, 2=10, 3=20}
3
[(1, a), (2, b), (3, c)]
[(1, a), (2, b)]
```

In the first line `groupBy` collected the two ankara orders in one list and left a one-element list for `izmir`. The second line is a map from id to amount. In the third line `3` is read for the `ankara` key: the order with id `1` lost its place when the one with id `3` arrived. The fourth line is a list of three pairs. In the fifth line the second list is shorter, so the element `3` stayed unpaired and the result is two pairs. `zip` never throws an exception; a mismatch in length is silently cut down to the shortest length. This behavior is what you want most of the time, but if the two lists must have the same length, that has to be checked before `zip`.

**CC8.** These measurements are for an order list of three elements. The differences between `groupBy` and `groupingBy`, `unzip` and a `zip` with three lists were not measured separately.

#### The Criterion: Content

The result of a transformation operation is a new collection. When we apply the same operation to the same collection twice, we get two results. These two results need not be the same object, but their contents must be equal. The block below applies the transformation twice for `map` and `filter`. `first` and `twin` are built separately by the same recipe; `other` differs in one respect: for `map` its last element is `41`, for `filter` it is missing one element. The last line asks whether the result of `map { it }`, which returns each element as it is, is the same object as the source list.

```kotlin
// Main.kt — the same transformation twice
fun main() {
    val amounts = listOf(5, 12, 7, 20)
    val checks = listOf(
        Criterion("map", amounts.map { it * 2 }, amounts.map { it * 2 }, listOf(10, 24, 14, 41)),
        Criterion("filter", amounts.filter { it > 6 }, amounts.filter { it > 6 }, listOf(12, 7)),
    )
    for (check in checks) check.report()
    summarize(checks)
    println(amounts.map { it } === amounts)
}
```

```
map: same-object=false, twin=true, other=false -> content
filter: same-object=false, twin=true, other=false -> content
singleton: 0
identity: 0
content: 2
partial-content: 0
false
```

Both measurements came out as **content**. The twins are separate objects (`same-object=false`) but equal; the different list is not equal. What defines the result of a transformation operation is not which call it came from but the elements it carries. This is the criterion of the lesson: **content**. The last line separates an important detail: even `map { it }`, which does not change the elements, does not hand back the source list; it builds a new one. Transformation operations do not touch the source collection; here the result was a separate object every time. For empty results the standard library may return a shared empty list; this was not measured here.

**CC9.** These measurements are for lists with `Int` elements. Whether a transformation applied to an empty collection returns the singleton empty list measured in the previous lesson was not measured separately.

#### The Bounding Measurement

The content criterion is easy while the elements are `Int`: two `5`s are equal to each other. The situation changes when the elements are objects that carry their own equality. `distinct()` and `toSet()` use **the element's own equality** to drop repeated elements; the transformation operation does not produce the criterion itself, it takes it from the element. The block below applies the same rule to three types. `Ticket` is a data class, and two `Ticket(1)`s are equal. `Plain` is an ordinary class, and two `Plain(1)`s are not equal unless they are the same object. `Session` is a data class, but its `visits` property is declared in the body and does not enter equality. `distinctBy`, on the other hand, compares not the element but the value the selector returns.

```kotlin
// Main.kt — distinct and toSet use the element's criterion
fun main() {
    val tickets = listOf(Ticket(1), Ticket(1), Ticket(2))
    val plains = listOf(Plain(1), Plain(1), Plain(2))
    println(tickets.distinct().size)
    println(plains.distinct().size)
    println(tickets.toSet().size)
    println(plains.toSet().size)
    println(plains.distinctBy { it.id }.size)
    val sessions = listOf(session(1, 0), session(1, 5))
    println(sessions.distinct().size)
    val checks = listOf(
        Criterion("data-class", Ticket(1), Ticket(1), Ticket(2)),
        Criterion("plain-class", Plain(1), Plain(1), Plain(2)),
        Criterion("body-property", session(1, 0), session(1, 0), session(1, 5)),
    )
    for (check in checks) check.report()
    summarize(checks)
}
```

```
2
3
2
3
2
1
data-class: same-object=false, twin=true, other=false -> content
plain-class: same-object=false, twin=false, other=false -> identity
body-property: same-object=false, twin=true, other=true -> partial-content
singleton: 0
identity: 1
content: 1
partial-content: 1
```

The bounding measurement is this: **`distinct` and `toSet` take the criterion from the element.** In the first four lines the same id sequence (`1`, `1`, `2`) is applied to two types. For `Ticket`, a data class, `distinct` and `toSet` reduce it to two elements: two `Ticket(1)`s count as equal and one is dropped. For `Plain`, an ordinary class, both leave three elements: `Plain(1)` and `Plain(1)` are separate objects and not equal, so none is dropped. In the fifth line `distinctBy { it.id }` reduces the same `Plain` list to two. The criterion here is the `Int` the selector returns, not the element itself. The sixth line shows a surprise for `Session`: although the visit counts are `0` and `5`, the two sessions reduce to one element, because `visits` is not in the constructor and so does not enter the data class's equality.

The next three lines name the same three cases with `Criterion`. For `Ticket` the criterion is **content**. For `Plain` the criterion is **identity**: even the twin is not equal. For `Session` the criterion is **partial content**: `other` differs only in `visits` and still comes out equal. The distribution splits 0/1/1/1 across three criteria. The criterion of the lesson is still content, but the limit is here: transformation operations do not bring a definition of equality, they use the element's equality. If the element's equality is built wrong, the removal of repeats in the result is built wrong too.

**CC10.** These measurements are for `Ticket` and `Plain` with one field each and `Session` with two fields. Which repeated element `distinct` keeps (the first or the last) was not asked in these measurements; the same goes for `distinctBy`.

#### Sorting and In-Place Change

A frequent mistake with transformation operations is to overlook the difference between `sorted` and `sort`. `sorted()` and `sortedBy` return a new sorted list and do not touch the source. `sort()` exists only on a mutable list, changes the list in place and returns nothing. Writing the first and not using its result means staying with the old list while expecting a sorted one. Also, `sortedBy` is stable: elements whose keys come out equal keep their order from the source. The block below measures these in turn. The order list is sorted by city and only the ids are printed; the stability among the two ankara orders can be read from this.

```kotlin
// Main.kt — sorted is a new list, sort is in place
fun main() {
    val numbers = mutableListOf(3, 1, 2)
    val ordered = numbers.sorted()
    println(numbers)
    println(ordered)
    println(ordered === numbers)
    numbers.sort()
    println(numbers)
    val orders = listOf(Order(1, "izmir", 10), Order(2, "ankara", 30), Order(3, "ankara", 20), Order(4, "bursa", 5))
    println(orders.sortedBy { it.city }.map { it.id })
    println(orders.sortedByDescending { it.amount }.map { it.id })
}
```

```
[3, 1, 2]
[1, 2, 3]
false
[1, 2, 3]
[2, 3, 4, 1]
[2, 3, 1, 4]
```

The first line shows that `numbers` stayed untouched after the `sorted()` call, the second that `ordered` is a new sorted list, and the third that the two are separate objects. After the `sort()` call `numbers` itself is sorted. In the fifth line sorting by city sets the order `ankara`, `bursa`, `izmir`, and the two ankara orders stay in their source order (`2`, then `3`); that is stability. The sixth line is sorting by amount in descending order.

**CC11.** These measurements are for `Int` and `Order` lists. A composite comparator with `sortedWith`, sorting elements that are not `Comparable`, and the cost of sorting very large lists were not measured.

#### Empty Collections and Edge Cases

The second source of errors in transformation operations is the edge: what happens if the collection is empty, or the requested piece is larger than the collection? `first()` throws an exception on an empty list, while `firstOrNull()` returns `null`. `maxOrNull()` gives `null` on an empty list. `average()` gives `NaN` on an empty list and throws no exception, so the result silently spoils later calculations. `chunked(n)` splits the list into pieces of at most `n` elements, and the last piece may stay short. `windowed(n)` builds a sliding window of `n` elements at each position; if the collection is shorter than `n` it produces no window at all.

```kotlin
// Main.kt — empty collection and chunking
fun main() {
    val empty = emptyList<Int>()
    println(empty.firstOrNull())
    println(empty.maxOrNull())
    println(empty.average())
    try {
        empty.first()
    } catch (e: NoSuchElementException) {
        println(e::class.simpleName)
    }
    val five = listOf(1, 2, 3, 4, 5)
    println(five.chunked(2))
    println(five.windowed(3))
    println(listOf(1, 2).windowed(3))
    println(listOf("1", "x", "3").mapNotNull { it.toIntOrNull() })
}
```

```
null
null
NaN
NoSuchElementException
[[1, 2], [3, 4], [5]]
[[1, 2, 3], [2, 3, 4], [3, 4, 5]]
[]
[1, 3]
```

The first two lines give `null`. In the third line the average of the empty list came out `NaN`: no exception, but code that takes the result for a number and uses it works wrongly. So the list must be checked for emptiness before taking an average. The fourth line shows that `first()` throws `NoSuchElementException` on an empty list. In the fifth line `chunked(2)` splits five elements into three pieces, `[1, 2]`, `[3, 4]`, `[5]`; the last piece is short. In the sixth line `windowed(3)` produces three windows: `[1, 2, 3]`, `[2, 3, 4]`, `[3, 4, 5]`. In the seventh line no window can be built for the two-element list and the result is an empty list. In the last line `mapNotNull` discards the element that gives `null` during conversion: `"x"` cannot be converted to a number, so only `1` and `3` remain. It is `map` and `filterNotNull` in one step.

**CC12.** These measurements are for `Int` and `String` lists. The `step` and `partialWindows` parameters of `windowed`, and the empty-case behavior of aggregating operations other than `average` on `Double` lists, were not measured.

#### Summary

- `map`, `filter`, `fold` and `reduce` do not touch the source collection; in this lesson's measurements the result was a separate object. On an empty collection `fold` gives the initial value and `reduce` throws an exception.
- `groupBy` builds a map from a key to a list, and `associate` and `associateBy` build a map from a key to a value; when keys collide the later entry replaces the earlier. `zip` cuts to the shorter list.
- Applying the same transformation twice to the same collection gives two equal but separate collections. The criterion of this lesson: **content**.
- The bounding measurement: `distinct` and `toSet` rest on the element's own equality. Repeats are removed among data class elements, are not removed among ordinary class elements, and a body property does not enter equality; `distinctBy` takes the criterion from the selector's value.

#### Next Step

This lesson assumed that every transformation operation runs immediately and builds a new collection at each step. If the intermediate lists in a chain are unnecessary, or the source is infinite, this assumption either makes the program do needless work or never ends. The next lesson runs the same operations with sequences (`Sequence`) and asks the equality question again there: are two sequences equal even if they produce the same elements?

### Sequences

Source: https://academia.sh/en/courses/kotlin/sequences

Measures that a sequence holds its intermediate operations until a terminal operation arrives and processes elements piece by piece, that it can be finished from an infinite source, and that two sequences are not counted as equal.

The previous lesson showed that every transformation takes a collection and gives a new collection. `map` builds a four-element list from a four-element list, `filter` builds a third from that list; each step in the chain leaves its own intermediate list. Most of the time this is no problem. But if the source is very large, or infinite, or only the first matching element is wanted, every step processing all the elements is needless work.

Kotlin offers the **sequence (`Sequence`)** for this situation. A sequence is not a container that stores elements but a recipe for producing them in order. The **intermediate operations** added on top of it, such as `map` and `filter`, compute nothing; they only add a step to the recipe and return a new sequence. The operations that start the computation and produce the result are called **terminal operations**: `toList`, `first`, `count` and the like. When a terminal operation arrives, the elements pass through the chain one by one: the first element goes through all the intermediate operations and reaches the result, and only then does the second element start. Laziness is not a measuring axis here, only a property that explains behavior: a category asking when the value is computed is not used in this course. The measuring question stays as in the earlier lessons: **what makes these two values equal?**

There is a name clash to watch for. In Kotlin an **array** (`Array`, `IntArray`) is a fixed-size structure that holds its elements in contiguous memory. A sequence is the `Sequence` interface and does not carry the same name; in this lesson "sequence" always means `Sequence`, and where the distinction is needed we write it in full. The four criteria, `Criterion` and `summarize` are as in the earlier lessons.

```kotlin
// Criterion.kt — classifies the criterion that makes two values equal
class Criterion(val label: String, first: Any?, twin: Any?, other: Any?) {
    val sameObject = first === twin
    val equalTwin = first == twin
    val equalOther = first == other

    val name: String = when {
        sameObject -> "singleton"
        !equalTwin -> "identity"
        !equalOther -> "content"
        else -> "partial-content"
    }

    fun report() {
        println("$label: same-object=$sameObject, twin=$equalTwin, other=$equalOther -> $name")
    }
}

fun summarize(checks: List<Criterion>) {
    for (name in listOf("singleton", "identity", "content", "partial-content")) {
        println("$name: ${checks.count { it.name == name }}")
    }
}
```

#### A List Works at Once, a Sequence Piece by Piece

The difference shows most clearly through a side effect. The block below applies the same two operations to the same four elements, first with a sequence and then with a list. Each lambda adds a record to `log`, so which step ran when can be read afterwards. For the sequence three things are asked in turn: how many records there are right after the chain is built, what `first()` returns, and in what order the records stand. For the list only the record count and the first element are asked.

```kotlin
// Main.kt — a list works at once, a sequence piece by piece
fun main() {
    val log = mutableListOf<String>()
    val lazy = sequenceOf(1, 2, 3, 4)
        .map { log.add("map$it"); it * 2 }
        .filter { log.add("filter$it"); it > 2 }
    println(log.size)
    println(lazy.first())
    println(log)

    val eagerLog = mutableListOf<String>()
    val eager = listOf(1, 2, 3, 4)
        .map { eagerLog.add("map$it"); it * 2 }
        .filter { eagerLog.add("filter$it"); it > 2 }
    println(eagerLog.size)
    println(eager.first())
}
```

```
0
4
[map1, filter2, map2, filter4]
8
4
```

The first line shows that when the chain was built no lambda ran in the sequence: the intermediate operations were added to the recipe, and the computation did not start. In the second line `first()` ran as the terminal operation and returned `4`. The third line says what ran. The first element `1` became `2` through `map1`, and `filter2` dropped it (`2 > 2` is not true). The second element `2` became `4` through `map2`, `filter4` let it pass, and `first()` stopped when it found the first match. The elements `3` and `4` were never processed. Four records were kept in all.

In the list the same chain left eight records: four `map`, four `filter`. The `4` in the fifth line gives the same result, but first all four elements were mapped, then all four were filtered, and only after that was `first()` called. The difference between them is not a difference of result but of **work**. The result comes out the same; the sequence does not process needless elements and builds no intermediate list. This also changes the order of records for lambdas with side effects: in the list all the `map`s run first, then all the `filter`s; in the sequence each element completes its own chain.

**CC13.** These measurements are for a four-element `Int` source and lambdas with side effects. How many elements the chain processes with pure lambdas without side effects, and how the amount of work changes with the size of the source, were not measured.

#### An Infinite Source and Kinds of Source

Because a sequence does not store its elements up front, its source can be infinite. `generateSequence(1) { it * 2 }` gives `1` as the first element, produces the next from the previous one and never ends. To get a finite result, a bounding operation such as `take` or `takeWhile` is added to the chain. The `sequence { ... }` builder takes a block that calls `yield`: each `yield` produces an element and the block pauses until the next element is requested. To move from a list to a sequence, `asSequence()` is used. The last two measurements show that the same sequence can be collected twice with `toList()`.

```kotlin
// Main.kt — an infinite source and kinds of source
fun main() {
    println(generateSequence(1) { it * 2 }.take(6).toList())
    println(generateSequence(1) { it + 1 }.map { it * it }.takeWhile { it < 50 }.toList())
    println(listOf(3, 8, 11).asSequence().filter { it > 5 }.count())
    val squares = sequence {
        var n = 1
        while (true) {
            yield(n * n)
            n++
        }
    }
    println(squares.drop(2).take(3).toList())
    val twice = sequenceOf(1, 2, 3).map { it + 1 }
    println(twice.toList())
    println(twice.toList())
}
```

```
[1, 2, 4, 8, 16, 32]
[1, 4, 9, 16, 25, 36, 49]
2
[9, 16, 25]
[2, 3, 4]
[2, 3, 4]
```

The first line takes six elements from the powers of two. The second line produces the squares of the natural numbers and stops at the first square that is fifty or larger; the last element is `49`. In the third line a list is turned into a sequence, filtered and counted: `8` and `11` are greater than five, so the result is `2`. The fourth line drops the first two elements of an infinite square producer written with `sequence { }` (`1` and `4`) and takes the next three. The last two lines show that the same sequence was collected twice and gave the same result twice. A sequence built with `sequenceOf` can be traversed more than once; each terminal operation runs the recipe from the start. This cannot be generalized to every sequence, but it holds for the sources of this lesson.

There is a limit here too: if the bounding operation is forgotten on an infinite source, the program does not finish. Calling `toList()` without an operation such as `take` or `takeWhile` tries to fit an infinite collection into memory. A sequence makes infinity possible, but leaves the responsibility of building a finite result to the caller.

**CC14.** These measurements are for the sources `generateSequence`, `sequence`, `sequenceOf` and `asSequence`. What sources that can be traversed only once, such as the seedless form of `generateSequence`, do on a second traversal, and the result of an infinite source without a bounding operation, were not measured separately.

#### The Criterion: Identity

Sequences do not store their own content; they carry a recipe. So the question of the equality of two sequences gets a different answer than for two lists. The block below builds two separate `sequenceOf(1, 2)`, compares the two with `==` and names the result with `Criterion`. `other` is a sequence that differs only in its second element. In the second measurement the sequences are built twice from the same list with `asSequence()`: even if the source is the same list, each call returns a new sequence.

```kotlin
// Main.kt — two sequences
fun main() {
    val list = listOf(1, 2)
    val checks = listOf(
        Criterion("sequenceOf", sequenceOf(1, 2), sequenceOf(1, 2), sequenceOf(1, 3)),
        Criterion("asSequence", list.asSequence(), list.asSequence(), sequenceOf(1, 3)),
    )
    for (check in checks) check.report()
    summarize(checks)
}
```

```
sequenceOf: same-object=false, twin=false, other=false -> identity
asSequence: same-object=false, twin=false, other=false -> identity
singleton: 0
identity: 2
content: 0
partial-content: 0
```

Both measurements came out as **identity**. The twins are separate objects and are not equal; that is, although two `sequenceOf(1, 2)` would produce the same elements, comparing them with `==` gives `false`. The reason is that `Sequence` does not define `equals`: equality falls to the default behavior, that is, identity. This is the criterion of the lesson: **identity**. The `other` measurement tells nothing here: since the twin does not come out equal anyway, the criterion becomes `identity` without looking at the `other` value. The second measurement is more instructive: even sequences built twice from the same `list` variable do not come out equal, because equality does not ask for the source to be the same.

**CC15.** These measurements are for sequences built with `sequenceOf` and `asSequence`. The equality among sequences derived with intermediate operations (the results of `map` and `filter`) and the equality of a `generateSequence` result were not measured separately.

#### The Bounding Measurement

Is there a way to compare two sequences, then? There is, but the comparison is made not on the sequence but on the collected form. `toList()` consumes the sequence and builds a list; because the list type defines `equals` by content, the result changes. The block below measures the same two sequences in three forms: as they are, with `toList()` and with `toSet()`. In the set measurement `other` is a sequence that gives the same elements in reverse order.

```kotlin
// Main.kt — content once collected
fun main() {
    val checks = listOf(
        Criterion("sequence", sequenceOf(1, 2), sequenceOf(1, 2), sequenceOf(1, 3)),
        Criterion("collected", sequenceOf(1, 2).toList(), sequenceOf(1, 2).toList(), sequenceOf(1, 3).toList()),
        Criterion("set", sequenceOf(1, 2).toSet(), sequenceOf(1, 2).toSet(), sequenceOf(2, 1).toSet()),
    )
    for (check in checks) check.report()
    summarize(checks)
}
```

```
sequence: same-object=false, twin=false, other=false -> identity
collected: same-object=false, twin=true, other=false -> content
set: same-object=false, twin=true, other=true -> partial-content
singleton: 0
identity: 1
content: 1
partial-content: 1
```

The bounding measurement is this: **equality is given not by the value but by the type that holds the value.** The same elements give the criterion `identity` in a sequence, `content` in a list and `partial-content` in a set. With the elements `1` and `2` in all three, the only thing that changes the criterion is the type of the container. The first line repeats the previous measurement. The second line shows that after `toList()` the twin is equal and the different one is not. The third line shows that order makes no difference in a set: even with `other` in reverse order, it comes out equal. So the criteria of the first two lessons appear side by side in this lesson.

The practical consequence: if two sequences have to be compared, they are collected into a suitable collection before the comparison. Which collection to collect into depends on which equality is wanted: `toList()` if order matters, `toSet()` if it does not. Also, collecting consumes the sequence; an infinite sequence has to be bounded before it is collected.

**CC16.** These measurements are for two-element sequences with `Int` elements. The equality of the collected forms of sequences whose elements are classes that do not define their own equality was not measured separately.

#### Operation Order Changes the Result

In a sequence the order of the intermediate operations changes not only the work but sometimes the result. The `filter` and `take` pair is the clearest example. `filter { it % 2 == 0 }.take(2)` first filters the even elements, then takes the first two. `take(2).filter { it % 2 == 0 }` first takes the first two elements, then filters only those. The block below applies both orders to a six-element source and records each `filter` call. The records show the elements that entered `filter`, in order.

```kotlin
// Main.kt — filter and take order
fun main() {
    val firstLog = mutableListOf<Int>()
    val filterFirst = sequenceOf(1, 2, 3, 4, 5, 6)
        .filter { firstLog.add(it); it % 2 == 0 }
        .take(2)
        .toList()
    println(filterFirst)
    println(firstLog)

    val secondLog = mutableListOf<Int>()
    val takeFirst = sequenceOf(1, 2, 3, 4, 5, 6)
        .take(2)
        .filter { secondLog.add(it); it % 2 == 0 }
        .toList()
    println(takeFirst)
    println(secondLog)
}
```

```
[2, 4]
[1, 2, 3, 4]
[2]
[1, 2]
```

The chain that filters first and takes after traversed until it found two even numbers: `filter` saw four elements and gave `[2, 4]`. In the chain that takes first and filters after, `filter` saw only the first two elements and a single element, `[2]`, remained. The two chains were built from the same operations; their results differ. This says that intermediate operations should not be treated as interchangeable.

**CC17.** These measurements are for the `filter` and `take` pair. The record order of `map` with `take`, and of other bounding operations such as `drop` and `takeWhile`, was not measured separately.

#### Sorting Breaks Laziness

Not every intermediate operation lets elements pass one by one. To sort a sequence, `sorted()` has to see all the elements: the first result element is known only after the smallest is found. So when `sorted()` enters the chain, the source is traversed from start to end even if the remaining operations are lazy. The measurement makes this visible with `onEach`; `onEach` leaves a record as it passes each element. The first chain has `sorted()`, the second does not, and both end with `first()`.

```kotlin
// Main.kt — sorted wants all the elements
fun main() {
    val sortedLog = mutableListOf<Int>()
    val smallest = sequenceOf(3, 1, 2, 5, 4)
        .onEach { sortedLog.add(it) }
        .sorted()
        .first()
    println(smallest)
    println(sortedLog)

    val plainLog = mutableListOf<Int>()
    val head = sequenceOf(3, 1, 2, 5, 4)
        .onEach { plainLog.add(it) }
        .first()
    println(head)
    println(plainLog)
}
```

```
1
[3, 1, 2, 5, 4]
3
[3]
```

The chain with `sorted()` found the smallest number, `1`, but to do so it saw all five elements. The record is the full order of the source. The chain without `sorted()` took the first element, `3`, and stopped; only that one is in the record. The rule is this: the gain of a sequence depends on no step in the chain waiting for all the elements. Operations such as `sorted`, `sortedBy` and `sortedDescending` cut this chain. If there is a bounding operation, putting it before the sorting reduces the elements to be sorted.

**CC18.** These measurements are for `sorted()` and `first()`. How many elements other operations such as `sortedBy`, `sortedDescending` and `distinct` wait for was not measured separately.

#### A Sequence Traversable Once

The previous section saw that a sequence built with `sequenceOf` can be collected twice. This does not hold for every sequence. A sequence built from an `Iterator` with `asSequence()` can give the iterator's elements only once; on the second traversal the same iterator is already exhausted. Kotlin counts these sequences as traversable once and throws `IllegalStateException` on the second terminal operation. The error appears when the sequence is used in two places; it is not visible in the code that builds the chain.

```kotlin
// Main.kt — a sequence traversable once
fun main() {
    val once = listOf(1, 2, 3).iterator().asSequence()
    println(once.toList())
    try {
        println(once.toList())
    } catch (e: IllegalStateException) {
        println(e::class.simpleName)
    }
    val again = listOf(1, 2, 3).asSequence()
    println(again.toList())
    println(again.toList())
}
```

```
[1, 2, 3]
IllegalStateException
[1, 2, 3]
[1, 2, 3]
```

The first collection gave `[1, 2, 3]`. The second collection threw `IllegalStateException`, which was caught. When the same elements were this time built with `listOf(1, 2, 3).asSequence()`, both collections gave the same result: the sequence traverses the source list from the start each time. The difference is whether the source can be traversed again. Code that will use a sequence more than once must know which source the sequence was built from; if it is not sure, it should take the intermediate result into a collection with `toList()` and use that.

**CC19.** These measurements are for `Iterator.asSequence()` and `List.asSequence()`. Making a sequence traversable once on purpose with `constrainOnce()` and the text of the error message were not measured.

#### Summary

- In a sequence the intermediate operations do not run until a terminal operation arrives; when it does, each element passes through the whole chain. A list processes all the elements at every step, while a sequence never processes needless elements.
- `generateSequence` and `sequence { }` build an infinite source; a finite result is taken with `take` or `takeWhile`. If the bounding operation is forgotten, the program does not finish.
- Two separately built sequences are not equal; `Sequence` does not define `equals`. The criterion of this lesson: **identity**.
- The bounding measurement: the same elements give `content` when collected with `toList()` and `partial-content` when collected with `toSet()`; the type that holds the value decides equality.

#### Next Step

This lesson showed that a sequence is a recipe, and that a recipe alone is not a value. When elements have to arrive over time, while some other work is awaited, the recipe itself has to wait too. The next lesson builds this waiting computation with coroutines and asks the equality question again: are two waiting jobs that will give the same result equal to each other?

### Coroutines

Source: https://academia.sh/en/courses/kotlin/coroutines

Measures that a coroutine yields its turn at the point where it is suspended, that the result is taken with await, and that two separately started jobs are not counted as equal.

In the previous lesson a sequence carried the recipe for a computation, and the computation ran when the terminal operation arrived. Some computations, though, wait for another job to finish: reading a file, a network response, a timer. During that wait the caller should not be held up. Kotlin offers the **coroutine** for this situation: a job can pause midway, release control and then continue where it left off.

Code that uses coroutines imports the coroutines library, `kotlinx.coroutines`. This library is not in the language itself but in a separate package; in this lesson and the next the line `import kotlinx.coroutines.*` is needed, which the earlier lessons did not need. Three constructs are enough for this lesson. A **suspending function** marked with `suspend` is a function that can pause in its body, and it is called only from a coroutine or from another suspending function. `launch` starts a new coroutine and returns a `Job`; the result is not awaited, only its completion is awaited with `join()`. `async` starts a new coroutine and returns a `Deferred` that carries the result; the result is taken with `await()`. All three must run inside a coroutine scope, which in this lesson `runBlocking` builds: it holds the thread that called the `main` function until all the coroutines inside it have finished.

A coroutine is not a thread. A thread is a unit of execution that the operating system schedules; a coroutine is a job that the library runs on a **dispatcher (`Dispatchers`)**, and it can run in turn on the same thread. In this lesson no measurement depends on time or on a thread name: timing is not read from the output, only the order and the results of the jobs. The measuring question stays as in the earlier lessons: **what makes these two values equal?**

```kotlin
// Criterion.kt — classifies the criterion that makes two values equal
class Criterion(val label: String, first: Any?, twin: Any?, other: Any?) {
    val sameObject = first === twin
    val equalTwin = first == twin
    val equalOther = first == other

    val name: String = when {
        sameObject -> "singleton"
        !equalTwin -> "identity"
        !equalOther -> "content"
        else -> "partial-content"
    }

    fun report() {
        println("$label: same-object=$sameObject, twin=$equalTwin, other=$equalOther -> $name")
    }
}

fun summarize(checks: List<Criterion>) {
    for (name in listOf("singleton", "identity", "content", "partial-content")) {
        println("$name: ${checks.count { it.name == name }}")
    }
}
```

#### Order: launch, join, async, await

The block below defines a suspending function named `doubled`; inside it the `yield()` call sets up a point where the coroutine can hand the turn to another. `main` measures three things. First, whether the `launch` block runs before or after the line that follows the `launch` call: both add a record to the same list. Second, collecting the results of two `async` jobs with `await()`. Third, taking in order the results of three jobs started inside `coroutineScope`.

```kotlin
// Main.kt — launch, async and await
import kotlinx.coroutines.*

suspend fun doubled(n: Int): Int {
    yield()
    return n * 2
}

fun main() = runBlocking {
    val log = mutableListOf<String>()
    val job = launch { log.add("launch") }
    log.add("main")
    job.join()
    println(log)
    val first = async { doubled(21) }
    val second = async { doubled(4) }
    println(first.await() + second.await())
    val total = coroutineScope {
        val parts = listOf(1, 2, 3).map { async { doubled(it) } }
        parts.map { it.await() }
    }
    println(total)
}
```

```
[main, launch]
50
[2, 4, 6]
```

The first line shows an important result: the `launch` block did not run as soon as the `launch` call returned. The `main` record was added first, and the `launch` record came after it. Because `runBlocking` works on a single thread, the new coroutine waits in the queue until the coroutine that started it reaches a suspension point; here that point is the `job.join()` call. That the order comes out this way depends on the dispatcher; so the lesson counts as guaranteed not this order but that both records are in the list after the `join()` call.

In the second line the two `async` jobs produce the results `21 * 2` and `4 * 2`; `await()` takes them and the total comes out `50`. In the third line `coroutineScope` does not return until all the jobs started inside it are finished; the results of the three `async`s are taken with `await()` in the list in the order they were started: `[2, 4, 6]`. The order of the results follows the starting order, because the `parts` list makes the `await()` calls in that order. Which of the jobs finished first cannot be read from this output, and no attempt is made to read it.

**CC20.** These measurements are for a `runBlocking` scope and a single `Int` result type. The order of the `launch` and `main` records on another dispatcher, the order in a function with no suspension point, and the real finishing order among the jobs were not measured.

#### The Criterion: Identity

`async` returns not a result but the promise of a result. This promise is a `Deferred` object, and each `async` call builds a new one. The block below compares two separate `async { 1000 }` calls with a third whose value differs. The second measurement asks the same question with a constant small value. In both, the blocks do not call `await()`, to see that the question concerns a `Deferred`, not a result.

```kotlin
// Main.kt — two Deferreds
import kotlinx.coroutines.*

fun main() = runBlocking {
    val checks = listOf(
        Criterion("async-1000", async { 1000 }, async { 1000 }, async { 2000 }),
        Criterion("async-1", async { 1 }, async { 1 }, async { 2 }),
    )
    for (check in checks) check.report()
    summarize(checks)
}
```

```
async-1000: same-object=false, twin=false, other=false -> identity
async-1: same-object=false, twin=false, other=false -> identity
singleton: 0
identity: 2
content: 0
partial-content: 0
```

**CC21.** These measurements are for `Deferred` objects built with `async`. The equality of `Job` objects built with `launch` and of promises built directly, such as `CompletableDeferred`, was not measured.

#### The Bounding Measurement

Taking the result changes the equality. The block below measures three things side by side: the `Deferred` objects themselves, their results taken with `await()`, and two `CoroutineName("a")` elements that can be put in the context of the same coroutine. `1000` was chosen for the results; small integers can be shared during boxing and could come out `singleton`, and for `1000` the lesson does not depend on this detail, the result being whatever the sandbox prints.

```kotlin
// Main.kt — result and context element
import kotlinx.coroutines.*

fun main() = runBlocking {
    val a = async { 1000 }
    val b = async { 1000 }
    val c = async { 2000 }
    val checks = listOf(
        Criterion("deferred", a, b, c),
        Criterion("result", a.await(), b.await(), c.await()),
        Criterion("name", CoroutineName("a"), CoroutineName("a"), CoroutineName("b")),
    )
    for (check in checks) check.report()
    summarize(checks)
}
```

```
deferred: same-object=false, twin=false, other=false -> identity
result: same-object=false, twin=true, other=false -> content
name: same-object=false, twin=true, other=false -> content
singleton: 0
identity: 1
content: 2
partial-content: 0
```

The bounding measurement is this: **equality is decided not by the job but by the type that carries the job's result.** A `Deferred` is compared by identity; the `Int` taken with `await()` is compared by content. The same jobs `a` and `b` were measured once as objects and once as results, and the criterion turned from `identity` to `content`. `CoroutineName` is also compared by content: two elements that carry the same name are equal, and one with a different name is not. This shows that an element in a coroutine context carries a value, not an identity.

The practical consequence: if two jobs have to be compared, the results should be compared, not the `Deferred`s; for that `await()` must be called first. That call too can be made only inside a coroutine; `await()` is a suspending function.

**CC22.** These measurements are for `Int` results and `CoroutineName` elements. The equality of other result types, of `Dispatchers` elements, and of the combination of several context elements was not measured.

#### Lazy Start and Cancellation

`async` and `launch` by default join the queue at once. If `start = CoroutineStart.LAZY` is given, the coroutine is built but not started: its body does not run at all until `await()`, `join()` or `start()` is called. This resembles the idea of the sequence in the previous lesson, but here what is lazy is not the result of a computation but the start of a job. Cancellation can end a job that has not started without it running at all. The block below first builds a lazy `async` and looks at it and at the list before and after `await()`. Then it builds a lazy `launch`, cancels it and only after that waits. The orders are read only from the points that the `await()` and `join()` calls guarantee.

```kotlin
// Main.kt — lazy start and cancellation
import kotlinx.coroutines.*

fun main() = runBlocking {
    val log = mutableListOf<String>()
    val lazy = async(start = CoroutineStart.LAZY) { log.add("body"); 7 }
    println(log.size)
    println(lazy.isActive)
    println(lazy.await())
    println(log)
    println(lazy.isCompleted)
    val cancelled = launch(start = CoroutineStart.LAZY) { log.add("never-runs") }
    cancelled.cancel()
    cancelled.join()
    println(cancelled.isCancelled)
    println(log)
}
```

```
0
false
7
[body]
true
true
[body]
```

When the lazy `async` was built the list was empty and the job was not active: the body did not run at all until the `await()` call. `await()` started the body, gave the result `7`, and the `body` record dropped into the list. After that the job counted as completed. The lazy `launch`, because it was cancelled, never entered its body: the `never-runs` record is not in the last line, but the job was marked `isCancelled`. The result is this: that a coroutine has been started and that it has been finished are different things, and `Job` lets you query this state. Cancelling a job whose result will never be used can also prevent the side effects of its body.

**CC23.** These measurements are for jobs built with `CoroutineStart.LAZY` in a `runBlocking` scope. The effect of cancellation in the middle of a running job and the order of starting with `start()` were not measured.

#### Waiting for All the Children

When several `launch`es are started, it is not necessary to wait for each one's completion separately. A `coroutineScope { }` block does not return until all the jobs started inside it are finished; `joinAll()` waits for all of those in a given list of `Job`s. The guarantee of both ways is the same: after the return, the side effects of the jobs are visible. But the order among the jobs is not part of this guarantee. So the block below measures not the order but the result: after the return it sorts the list and counts how many elements it holds.

```kotlin
// Main.kt — coroutineScope and joinAll
import kotlinx.coroutines.*

fun main() = runBlocking {
    val log = mutableListOf<Int>()
    coroutineScope {
        for (i in 1..3) {
            launch {
                yield()
                log.add(i)
            }
        }
    }
    println(log.sorted())
    val jobs = List(4) { i ->
        launch {
            yield()
            log.add(i * 10)
        }
    }
    jobs.joinAll()
    println(log.size)
    println(jobs.all { it.isCompleted })
}
```

```
[1, 2, 3]
7
true
```

When the `coroutineScope` block returned, all three records were in the list. The measurement printed the sorted list, because the order in which the jobs finish is not guaranteed. In the second group four jobs were awaited with `joinAll()` and the list held seven elements in all; all of them appeared completed. The distinction between guarantee and order matters in practice: a scope or `join` is used to wait for a result, and logic that depends on the finishing order is built wrongly.

**CC24.** These measurements are for `launch` jobs and an `Int` list. The behavior of the scope and the sibling jobs when a child job throws an exception was not measured in this lesson.

#### Context and Name

Every coroutine carries a context, and context elements are read by key. `coroutineContext[CoroutineName]` gives the name if there is one, and `null` if not. Inside a `withContext(CoroutineName("worker")) { }` block the context carries the new name, and when the block ends the old context returns. If a context element is given to an `async` or `launch` call, the child job carries it. The block below defines a small suspending function that reads the name and calls it from four places. The name is not a thread name; it is only a label put in the context, which is why it can be measured.

```kotlin
// Main.kt — reading the name from the context
import kotlinx.coroutines.*
import kotlin.coroutines.coroutineContext

suspend fun currentName(): String? = coroutineContext[CoroutineName]?.name

fun main() = runBlocking {
    println(currentName())
    println(withContext(CoroutineName("worker")) { currentName() })
    println(currentName())
    val named = async(CoroutineName("child")) { currentName() }
    println(named.await())
}
```

```
null
worker
null
child
```

In the first call there was no name in the `runBlocking` context and the result is `null`. In the `withContext` block the name became `worker`. After the block ended the name is `null` again: the context change was specific to the block. In the last line the child `async` read its own name, `child`. This is why the context is a clean way to carry information between coroutines: without passing a parameter, but with a scope that is defined in a specific way.

**CC25.** These measurements are for a `CoroutineName` element and a `runBlocking` scope. Reading a `Job` element or a `Dispatchers` element from the context, a child inheriting elements from the parent context, and combining two elements with `+` were not measured.

#### Job States and Repeated await

Every `launch` and `async` carries a `Job`, and the `Job` says at what stage the work is with three properties: `isActive` shows that it is in progress, `isCompleted` that it has finished, `isCancelled` that it has been cancelled. To say that a job has finished is guaranteed after `join()` or `await()` returns; the intermediate states between starting and the first suspension point depend on the dispatcher and are not measured in this lesson. `await()` can also be called more than once: the result is computed once and later calls give the same value. The block below measures first the state after `join()`, then two `await()` calls on the same `Deferred` and how many times the body ran.

```kotlin
// Main.kt — job states and repeated await
import kotlinx.coroutines.*

fun main() = runBlocking {
    val job = launch { yield() }
    job.join()
    println(job.isActive)
    println(job.isCompleted)
    println(job.isCancelled)
    var runs = 0
    val deferred = async { runs++; "result" }
    println(deferred.await())
    println(deferred.await())
    println(runs)
}
```

```
false
true
false
result
result
1
```

After `join()` returned, the job was no longer active, it was completed and it had not been cancelled. The `Deferred` was awaited twice and gave the same value both times; the counter rose once, so the body ran only once. `await()` does not restart the job, it reads the result. This also means that once the result of a `Deferred` is computed it is stored, and every `await()` reads that stored value. This is the opposite of the flow case: a cold flow runs again at each collecting, while a `Deferred` shares the result of a single computation.

**CC26.** These measurements are for the states after `join()` and `await()`. The `isActive` value between starting and the first suspension, and the state of a job that ends with an error, were not measured.

#### Summary

- A function marked `suspend` is called only from a coroutine or from another suspending function; `launch` starts a job without a result, `async` starts a job with a result.
- A `launch` call does not run at once; inside `runBlocking` it waits until the coroutine that started it reaches a suspension point. `join()` and `await()` guarantee that the job has finished.
- The `Deferred` objects of two separate `async` calls are not equal. The criterion of this lesson: **identity**.
- The bounding measurement: the `await()` results of the same jobs give the `content` criterion; `CoroutineName` elements are also compared by content.

#### Next Step

This lesson dealt with jobs that produce a single value. A job can instead let values flow over time. The next lesson builds this with flows and asks the equality question there too: are two flows that emit the same values equal, and how are equal values that arrive one after another handled in a flow?

### Flows

Source: https://academia.sh/en/courses/kotlin/flows

Measures that a cold flow reruns on every collecting, that consecutive equal values are dropped by content, and that two flows are not counted as equal.

The previous lesson dealt with jobs that produce a single value. Most jobs produce not one value but a sequence of values that arrive over time: readings from a sensor, the pages of a list, each step of a counter. A sequence cannot wait for such values; while producing an element it blocks the caller. A **flow (`Flow`)** builds the same idea with coroutines: it produces values one at a time, and the producer and the collector can be suspended in between.

A flow has two faces. A **cold flow** is built with `flow { }` or `flowOf` and does nothing on its own; it waits until someone collects it. For every collector the producer block runs from the start. A **hot flow** carries a state independent of the collector; in this lesson only `MutableStateFlow` appears, a hot flow that holds a last value. `collect` is used for collecting; to reduce all the values of a flow to a list, `toList()` is enough. Both are suspending functions and are called inside a coroutine; this lesson again sets up a `runBlocking` scope, and next to `import kotlinx.coroutines.*` the flows need `import kotlinx.coroutines.flow.*`. Timing is not measured, only values and counts are read. The measuring question is as in the earlier lessons: **what makes these two values equal?**

```kotlin
// Criterion.kt — classifies the criterion that makes two values equal
class Criterion(val label: String, first: Any?, twin: Any?, other: Any?) {
    val sameObject = first === twin
    val equalTwin = first == twin
    val equalOther = first == other

    val name: String = when {
        sameObject -> "singleton"
        !equalTwin -> "identity"
        !equalOther -> "content"
        else -> "partial-content"
    }

    fun report() {
        println("$label: same-object=$sameObject, twin=$equalTwin, other=$equalOther -> $name")
    }
}

fun summarize(checks: List<Criterion>) {
    for (name in listOf("singleton", "identity", "content", "partial-content")) {
        println("$name: ${checks.count { it.name == name }}")
    }
}
```

#### Cold Flow, Transformation and Hot Flow

The block below first builds a flow with `flow { }`; each time the producer block runs it raises a counter. The flow is collected twice and the counter is read. Then transformation with `map` and `filter` and collecting with `toList()` are shown. Finally a `MutableStateFlow` is built, its value is changed and read with `value`.

```kotlin
// Main.kt — cold flow and hot flow
import kotlinx.coroutines.*
import kotlinx.coroutines.flow.*

fun main() = runBlocking {
    var runs = 0
    val numbers = flow {
        runs++
        emit(1)
        emit(2)
        emit(3)
    }
    println(runs)
    println(numbers.toList())
    println(numbers.toList())
    println(runs)
    println(flowOf(1, 2, 3, 4).map { it * 10 }.filter { it > 10 }.toList())
    val state = MutableStateFlow(5)
    state.value = 6
    println(state.value)
}
```

```
0
[1, 2, 3]
[1, 2, 3]
2
[20, 30, 40]
6
```

The first line shows that when the flow was built the producer block did not run at all: `runs` is zero. The two `toList()` calls each give `[1, 2, 3]`, and the fourth line says that the producer block ran **twice**. This is the definition of a cold flow: every collector runs the flow from the start, and the flow remembers nothing from the previous collecting. On the fifth line `map` multiplies each value by ten, `filter` drops the ten, and `[20, 30, 40]` remains; as with the sequence, the intermediate operations only describe the flow and run at collecting time. On the last line `MutableStateFlow` holds a value and the last assigned value is read with `value`: `6`.

**CC27.** These measurements are for single-collector flows and a single-threaded `runBlocking` scope. Several collectors collecting at the same time, changing the dispatcher with `flowOn`, and cancelling a flow from outside with `cancel` were not measured; cutting a flow short with `take` is measured in a later section.

#### The Criterion: Content

`MutableStateFlow` and `distinctUntilChanged` ask the same question: is the newly arrived value equal to the previous one? If it is equal, they skip it. This question is the measuring question of this course, so in a flow the criterion decides equality. Below, two helper classes are defined first: `Reading` is a data class, and `Raw` is a plain class with the same field.

```kotlin
// Reading.kt — data class
data class Reading(val id: Int)
```

```kotlin
// Raw.kt — plain class
class Raw(val id: Int)
```

The block below applies `distinctUntilChanged()` to a `flowOf(A, A, B, B, A)` flow with `Reading` elements and counts how many values remain. Right beside it a `Criterion` measurement is made for `Reading`.

```kotlin
// Main.kt — consecutive equality
import kotlinx.coroutines.*
import kotlinx.coroutines.flow.*

fun main() = runBlocking {
    val a = Reading(1)
    val b = Reading(2)
    val kept = flowOf(a, Reading(1), b, Reading(2), Reading(1)).distinctUntilChanged().toList()
    println(kept.size)
    Criterion("data-class", Reading(1), Reading(1), Reading(2)).report()
}
```

```
3
data-class: same-object=false, twin=true, other=false -> content
```

Of the five values, three remained: `A, B, A`. The second `Reading(1)` and the second `Reading(2)`, which arrived right after an equal value, were dropped, because `distinctUntilChanged` compares with the previous value using `==` and a data class defines `equals` by its fields. `Reading(1)`, a separate object, was counted as equal to the previous one. The second line says the same result with `Criterion`: the twin is equal, the other is not, and the criterion is **content**. This is the criterion of this lesson: **content**.

**CC28.** These measurements are for a data class with an `Int` field and a `flowOf` with five elements. `distinctUntilChangedBy`, which selects a key, the `distinctUntilChanged { a, b -> ... }` form, which takes a comparator, and how many values can be dropped in long flows were not measured.

#### The Bounding Measurement

A block that measures the same flow with plain-class elements, assigning an equal value to a `MutableStateFlow`, and two separate flows themselves:

```kotlin
// Main.kt — plain class, state and flow
import kotlinx.coroutines.*
import kotlinx.coroutines.flow.*

fun main() = runBlocking {
    val raws = flowOf(Raw(1), Raw(1), Raw(2), Raw(2), Raw(1)).distinctUntilChanged().toList()
    println(raws.size)
    val original = Reading(1)
    val state = MutableStateFlow(original)
    state.value = Reading(1)
    println(state.value === original)
    val checks = listOf(
        Criterion("flow", flowOf(1, 2), flowOf(1, 2), flowOf(1, 3)),
        Criterion("data-class", Reading(1), Reading(1), Reading(2)),
        Criterion("plain-class", Raw(1), Raw(1), Raw(2)),
    )
    for (check in checks) check.report()
    summarize(checks)
}
```

```
5
true
flow: same-object=false, twin=false, other=false -> identity
data-class: same-object=false, twin=true, other=false -> content
plain-class: same-object=false, twin=false, other=false -> identity
singleton: 0
identity: 2
content: 1
partial-content: 0
```

The first line shows that the same `distinctUntilChanged` call drops no value from the plain-class elements: all five of the five elements remained. Even though `Raw(1)` and `Raw(1)` carry the same field as content, `equals` falls back to identity and consecutive values are never counted as equal. On the second line a separate but equal object, `Reading(1)`, was assigned to the `MutableStateFlow`, and `value` still points to the first object (`true`): the flow did not count the equal value as a new value and left the old one in place. The hot flow, like the cold flow, measures equality with `==`.

The remaining lines put three measurements side by side. Two separate `flowOf(1, 2)` flows give the **identity** criterion: a flow itself does not define equality by content, just like the sequence and the `Deferred`. The data class comes out `content`, the plain class `identity`. The bounding measurement is this: **the values inside a flow follow the content criterion only if the type of the element carries it.** The recipe that builds the flow is compared by identity, while the value passing through the flow is compared by the equality of its type. The summary table says the same: two `identity` (the flow and the plain class), one `content` (the data class).

**CC29.** These measurements are for `MutableStateFlow` and cover assigning an equal value with `value`. The values a `StateFlow` delivers to a collector and `SharedFlow` were not measured; assignments with `update` and `compareAndSet` are measured in a later section.

**CC30.** In this lesson no timing, thread name or dispatcher order was printed. The values of the flows were collected with `toList()`, and the order in the output is the producer's order.

#### Cutting a Flow Short

The producer of a flow can produce many values, but the collector may not want all of them. `take(n)` takes the first `n` values from the flow and then cancels the flow; the producer block does not continue past that point. It is the same idea as `take` on a sequence, but here what is cut is a coroutine. The block below builds a flow ready to produce five values. The producer leaves a record before each value, so after `take(2)` how far the producer got can be read from the list. In addition, the start and the end of the flow are recorded with `onStart` and `onCompletion`.

```kotlin
// Main.kt — take cuts the flow short
import kotlinx.coroutines.*
import kotlinx.coroutines.flow.*

fun main() = runBlocking {
    val log = mutableListOf<String>()
    val source = flow {
        for (i in 1..5) {
            log.add("produce$i")
            emit(i)
        }
    }
    val firstTwo = source
        .onStart { log.add("start") }
        .onCompletion { log.add("done") }
        .take(2)
        .toList()
    println(firstTwo)
    println(log)
}
```

```
[1, 2]
[start, produce1, produce2, done]
```

The collected list became `[1, 2]`, so it holds only two values. The record list opens with `start`, continues with `produce1` and `produce2`, and closes with `done`; there is no `produce3` record at all. The records show that the producer never reached for the third value: `take(2)` cut the flow after taking the second value. The `onStart` and `onCompletion` records are also in the list, and the completion record drops on a cut flow as well. This order is guaranteed, because when `toList()` returns the flow is complete; the records are read after this point. The practical consequence is this: if the first few values of a flow are enough, using `take` or `first()` does not run the rest of the producer block. Where the producer does work that has a side effect, this difference is the difference between work that runs and work that does not.

**CC31.** These measurements are for `take`, `onStart` and `onCompletion`, with a single collector. The `try/finally` cleanup in the producer block while the flow is being cut, and cutting with `takeWhile`, were not measured.

#### Terminal Operations and the Empty Flow

Turning a flow into a list is not the only way. `fold` and `reduce` reduce all the values to a single value, `count` gives the number of values, and `first()` returns the first value. The empty flow is the boundary case: `first()` throws an exception, while `firstOrNull()` gives `null`. The block below calls these operations on a flow with four values; then it tries two of them on an empty flow. `fold` asks for an initial value; `reduce` counts the first value as the initial value, and cannot work on an empty flow.

```kotlin
// Main.kt — terminal operations and the empty flow
import kotlinx.coroutines.*
import kotlinx.coroutines.flow.*

fun main() = runBlocking {
    val numbers = flowOf(1, 2, 3, 4)
    println(numbers.fold(100) { sum, n -> sum + n })
    println(numbers.reduce { sum, n -> sum + n })
    println(numbers.count { it % 2 == 0 })
    println(numbers.first())
    val empty = emptyFlow<Int>()
    try {
        empty.first()
    } catch (e: NoSuchElementException) {
        println(e::class.simpleName)
    }
    println(empty.firstOrNull())
}
```

```
110
10
2
1
NoSuchElementException
null
```

`fold` added the four values to its initial value of `100`. `reduce` started from the first value without an initial value and gave only the sum of the four values. `count` counted only the even ones. `first()` is the first value of the flow. On the empty flow `first()` threw `NoSuchElementException`, while `firstOrNull()` returned `null`. This is the flow counterpart of the `first` and `firstOrNull` pair on collections: the first is chosen if the error should be visible, the second if emptiness is acceptable.

**CC32.** These measurements are for finite flows of `Int` values. Calling `fold` and `count` on a flow that never ends, and the class name of the exception that `reduce` throws on an empty flow, were not measured.

#### Transforming and Combining

`map` produces one value from each value. To produce more than one value or no value from a value, `transform` is used: the block takes the incoming value and calls `emit` as many times as it likes. To read two flows side by side, `zip` takes one value at a time from the two flows and combines them; when the shorter flow ends, the result ends too. The block below measures both. The second flow carries two values instead of three.

```kotlin
// Main.kt — transform and zip
import kotlinx.coroutines.*
import kotlinx.coroutines.flow.*

fun main() = runBlocking {
    val doubled = flowOf(1, 2, 3).transform { n ->
        emit(n)
        if (n % 2 == 1) emit(n * 10)
    }.toList()
    println(doubled)
    val pairs = flowOf(1, 2, 3).zip(flowOf("a", "b")) { n, s -> "$n$s" }.toList()
    println(pairs)
}
```

```
[1, 10, 2, 3, 30]
[1a, 2b]
```

`transform` produced two values for the odd numbers and one value for the even number. The result list carries the input values and the derived values one after another. `zip` paired one value from each of the two flows and produced `"1a"` and `"2b"`; because the third value of the first flow was left without a partner, it produced no result. The result of these two operations depends not on timing but only on the input values and their order, so they can be measured safely. The rule is clear: `map` does a one-to-one, `transform` a one-to-many or one-to-zero transformation; `zip` stops at the length of the shorter one.

**CC33.** These measurements are for `transform` and `zip`, with two finite flows. `combine`, which joins the latest values of two flows as they change over time, and `flatMapConcat` were not measured; their result can depend on timing.

#### Updating the State

Assigning with `value` is enough to put a value into a `MutableStateFlow`, but the gap between reading and assigning is not safe. The `update { }` block computes the new value from the old one and makes the assignment in a single step. `compareAndSet(expected, new)` assigns only if the current value is equal to the expected one, and returns whether it assigned. The comparison is with `==`, so these two operations also depend on the equality rule of the earlier sections. The block below updates a number and checks it.

```kotlin
// Main.kt — update and compareAndSet
import kotlinx.coroutines.*
import kotlinx.coroutines.flow.*

fun main() = runBlocking {
    val state = MutableStateFlow(0)
    state.update { it + 1 }
    println(state.value)
    println(state.compareAndSet(1, 5))
    println(state.value)
    println(state.compareAndSet(1, 9))
    println(state.value)
    println(state.first())
}
```

```
1
true
5
false
5
5
```

`update` raised the value from `0` to `1`. The first `compareAndSet(1, 5)` assigned, because the current value was `1`, and returned `true`. The second `compareAndSet(1, 9)` did not assign, because the value was now `5`, and returned `false`; the value stayed `5`. `first()` gave the current value of the hot flow: a state flow, when collected, always starts with the current value. This separates `update` and `compareAndSet` from a plain `value` assignment: both read the current value and decide, and they do it in a single operation. This is the difference from a cold flow: a hot flow has a value in hand, and gives it first to a new collector.

**CC34.** These measurements are for a `MutableStateFlow` of `Int` values and a single coroutine. Several coroutines calling `update` at the same time, and whether a `StateFlow` collector sees the intermediate values, were not measured.

#### Summary

- A cold flow runs the producer block from the start on every collecting; nothing runs when it is built. `map` and `filter` transform the flow, `toList()` collects it.
- `MutableStateFlow` is a hot flow that holds the last value; when an equal value is assigned, the old object stays in place.
- `distinctUntilChanged` drops consecutive equal values with `==`. The criterion of this lesson: **content**. With the data class 3 of 5 values remain, with the plain class all 5.
- The bounding measurement: the flow itself is compared by identity, and the values inside it by the equality of their type.

#### Next Step

Until now every lesson asked whether a value is equal. The last lesson asks whether an error is equal instead of a value: are two exceptions that carry the same message equal? The Exception Handling lesson measures this with try, catch and finally.

### Exception Handling

Source: https://academia.sh/en/courses/kotlin/exception-handling

Measures the unchecked exception model, the try expression, Result and propagation in a coroutine, shows that two exceptions with the same message are not counted as equal, and closes the course.

The previous lesson measured that two flows are not counted as equal. This last lesson asks, instead of a value, whether an error is equal: are two exceptions that carry the same message equal? First the error model of Kotlin is read. In Java a function declares with `throws` which exceptions it may throw, and the caller has to catch them. In Kotlin there is no such declaration; all exceptions count as **unchecked exceptions**, and catching is up to the caller. **Throwing** is done with `throw`, **catching** with `try` and `catch`. The measuring question is again the same: **what makes these two values equal?**

```kotlin
// Criterion.kt — classifies the criterion that makes two values equal
class Criterion(val label: String, first: Any?, twin: Any?, other: Any?) {
    val sameObject = first === twin
    val equalTwin = first == twin
    val equalOther = first == other

    val name: String = when {
        sameObject -> "singleton"
        !equalTwin -> "identity"
        !equalOther -> "content"
        else -> "partial-content"
    }

    fun report() {
        println("$label: same-object=$sameObject, twin=$equalTwin, other=$equalOther -> $name")
    }
}

fun summarize(checks: List<Criterion>) {
    for (name in listOf("singleton", "identity", "content", "partial-content")) {
        println("$name: ${checks.count { it.name == name }}")
    }
}
```

#### The Unchecked Exception and the try Expression

The block below measures two things. `readDisk` throws Java's `IOException`, but there is no declaration in its signature and the compiler asks for nothing. `try`, on the other hand, is an expression: the block gives the value of its last expression, the `catch` branch gives its own value, and `finally` runs in every case but produces no value. The log list shows the order.

```kotlin
// Main.kt — unchecked exception, try expression and finally
import java.io.IOException

fun readDisk(): String {
    throw IOException("disk")
}

fun withdraw(balance: Int, amount: Int): Int {
    if (amount > balance) throw IllegalStateException("insufficient")
    return balance - amount
}

fun main() {
    val log = mutableListOf<String>()
    val left = try {
        log.add("try")
        withdraw(100, 500)
    } catch (e: IllegalStateException) {
        log.add("catch")
        -1
    } finally {
        log.add("last")
    }
    println(left)
    println(log)
    try {
        readDisk()
    } catch (e: IOException) {
        println(e::class.simpleName)
    }
    println(withdraw(100, 30))
}
```

```
-1
[try, catch, last]
IOException
70
```

The first line is `-1`: because `withdraw` threw, the value of the `try` block was discarded and the `-1` of the `catch` branch was assigned to `left`. The log tells the order `[try, catch, last]`; `finally` ran last, after the value was settled. The third line gives the name `IOException`; it was caught because a `catch` was written, and the compilation would have passed even if it had not been. The last line shows that the path that does not throw returns `70`.

**CC35.** These measurements are for `try` expressions with a single thread and no `return`. The result of a `return` or a new exception thrown from inside `finally`, closing a resource with `use`, and the `Error` classes were not measured.

#### runCatching and Result

To carry an error as a value instead of catching it, `runCatching { }` is used. If the block returned, the `Result` is successful; if it threw, it is failed. `getOrElse` gives a fallback value on failure, and `exceptionOrNull` gives `null` if successful and the exception itself if not. First, **an exception class of its own** is defined in a helper file; the main block builds a function that throws it.

```kotlin
// Errors.kt — an exception class of its own
class LimitException(val limit: Int) : Exception("limit exceeded")
```

```kotlin
// Main.kt — runCatching and Result
fun parse(text: String): Int {
    if (text.length > 3) throw LimitException(3)
    return text.toInt()
}

fun main() {
    val ok = runCatching { parse("42") }
    val badFormat = runCatching { parse("x") }
    val tooLong = runCatching { parse("12345") }
    println(ok.getOrElse { -1 })
    println(badFormat.getOrElse { -1 })
    println(ok.exceptionOrNull())
    println(badFormat.exceptionOrNull()?.let { it::class.simpleName })
    val error = tooLong.exceptionOrNull()
    if (error is LimitException) {
        println(error.limit)
    }
}
```

```
42
-1
null
NumberFormatException
3
```

Because `ok` is successful, `getOrElse` gave `42`; because `"x".toInt()` threw `NumberFormatException`, the second line is the fallback value `-1`. The third line shows that on a successful result `exceptionOrNull` is `null`, and the fourth line shows the type of the exception on a failed result. The last line reads the `limit` value that the exception class of its own carries: after the `is` check, `error` becomes `LimitException` by smart cast.

**CC36.** These measurements are for `runCatching` calls with an `Int` result and a class under `Exception`. Chains of `map`, `recover` and `onFailure`, `getOrThrow`, and using `Result` as the return type of a function were not measured.

#### Propagation in a Coroutine

In structured concurrency, if a child coroutine fails, the scope is cancelled: the siblings receive the cancellation and the exception comes out of `coroutineScope`. In the block below the first child will wait a thousand milliseconds and the second will throw at once. Only the type of the exception caught outside is printed, not its message.

```kotlin
// Main.kt — propagation of an exception in a coroutine
import kotlinx.coroutines.*

fun main() = runBlocking {
    val log = mutableListOf<String>()
    val caught = try {
        coroutineScope {
            launch {
                try {
                    delay(1000)
                    log.add("done")
                } finally {
                    log.add("cancelled")
                }
            }
            launch {
                throw IllegalStateException("sibling")
            }
        }
        "none"
    } catch (e: Exception) {
        e::class.simpleName
    }
    println(caught)
    println(log)
}
```

```
IllegalStateException
[cancelled]
```

The exception caught outside is the `IllegalStateException` that the child threw. In the log there is only `cancelled`: the cancellation reached the waiting child, the `finally` block ran, and `done` was never written. The `"none"` line after the `coroutineScope` call was never reached. The exception goes past the scope of the coroutine and can be caught with an ordinary `try`.

**CC37.** These measurements are for a single `coroutineScope`, a single `runBlocking` and one failed child. `supervisorScope`, `CoroutineExceptionHandler`, and how the exceptions of several failed children are gathered were not measured.

#### The Criterion: Identity

Now the real question. Are two exceptions built with the same message equal? The `Throwable` class does not define `equals`, so it falls back to identity. The bounding measurements add `Result` and an exception written as a data class. `Result.failure` carries the exception inside: two separate exceptions make two separate `Result`s, and the same exception makes two `Result`s equal.

```kotlin
// Main.kt — the criterion of an exception and the bounding measurements
data class CodedError(val code: Int) : Exception("code")

fun main() {
    val shared = IllegalStateException("m")
    val checks = listOf(
        Criterion("exception", IllegalStateException("m"), IllegalStateException("m"), IllegalStateException("x")),
        Criterion("result-two-exceptions", Result.failure<Int>(IllegalStateException("m")), Result.failure<Int>(IllegalStateException("m")), Result.failure<Int>(IllegalStateException("x"))),
        Criterion("result-same-exception", Result.failure<Int>(shared), Result.failure<Int>(shared), Result.failure<Int>(IllegalStateException("x"))),
        Criterion("result-success", Result.success(1), Result.success(1), Result.success(2)),
        Criterion("data-class-exception", CodedError(7), CodedError(7), CodedError(8)),
    )
    for (check in checks) check.report()
    summarize(checks)
}
```

```
exception: same-object=false, twin=false, other=false -> identity
result-two-exceptions: same-object=false, twin=false, other=false -> identity
result-same-exception: same-object=false, twin=true, other=false -> content
result-success: same-object=false, twin=true, other=false -> content
data-class-exception: same-object=false, twin=true, other=false -> content
singleton: 0
identity: 2
content: 3
partial-content: 0
```

The first line is the criterion of this lesson: two exceptions with the same message are not equal, and the criterion is **identity**. On the second line the `Result`s that wrap two separate exceptions are not equal either. On the third line two separate `Result` objects that wrap the same exception are equal: `Result` inherits the criterion of the exception. `Result.success(1)` gives content twice. `CodedError`, written as a data class, is equal with the same code, because the generated `equals` redefines the identity equality inherited from `Exception`. The bounding measurement is this: **the criterion of an exception is decided by its type, not by its message.**

**CC38.** These measurements are for `IllegalStateException` and `Result`. The `cause` chain of an exception, the contents of `stackTrace`, and other exception classes that have their own `equals` were not measured.

#### Summary

- Kotlin has no `throws`; all exceptions are unchecked exceptions. `try` is an expression that produces a value, and `finally` runs last.
- `runCatching` carries the error as a `Result`; `getOrElse` gives a fallback value, and `exceptionOrNull` gives the exception.
- Inside `coroutineScope`, when one child fails, the siblings are cancelled and the exception propagates outward.
- Two exceptions with the same message are not equal. The criterion of this lesson: **identity**.
- The bounding measurement: `Result.failure` carries the criterion of the exception; an exception written as a data class is content with the same fields.

#### Course Wrap-Up

In twenty-three lessons the course asked a single question, and the criterion of each lesson was taken from the summary of the source lesson:

| Lesson | Measured value | Criterion | Bounding measurement |
|---|---|---|---|
| Kotlin's Place | data class, JVM list | content | not equal if `name` in the constructor differs |
| Variables | `const val` constant | singleton | the same text built at run time is content |
| Basic Types | boxed `1000` | content | 127 is singleton; NaN changes with the static type |
| String Templates | two separate templates | content | a template with one variable is singleton |
| Control Flow | `when` branch, plain class | identity | `is Box` matches; two `1..3` are content |
| Functions | two `::plain` | content | the `listOf()` default is singleton |
| Lambdas and Higher-Order Functions | two separate lambdas | identity | a non-capturing lambda is singleton; `::inc` is content |
| Scope Functions | `let` with `copy` | content | the twin of `also` is singleton and spoils the measurement |
| Null Safety | `null` with `null` | singleton | two filled strings are content; filled with `null` is identity |
| Safe Calls and the Elvis Operator | two safe calls | content | on an empty receiver two `null`s are singleton |
| Classes and Constructors | class without `equals` | identity | the same pattern is content, partial content by `equals` |
| Data Classes | object built with `copy` | content | an `IntArray` field is identity, a `List` field is content |
| Sealed Types and Enumerations | enumeration entry | singleton | the kind of the subtype decides the criterion |
| Object Declarations | two reads | singleton | an object expression is new each time, identity |
| Interfaces and Delegation | two objects delegating with `by` | identity | two data classes with the same delegate are equal |
| Extension Functions | extension `equals` | identity | the member beats the extension |
| Generics and Variance | two empty lists, two type parameters | singleton | a generic data class looks at the field value |
| Collection Types | list equality | content | set order is partial content; the empty list is singleton |
| Transformation Operations | two `map`s | content | `distinct` uses the criterion of the element |
| Sequences | two `sequenceOf`s | identity | content with `toList()` |
| Coroutines | two `Deferred`s | identity | `await` results are content |
| Flows | `distinctUntilChanged` | content | with a plain class 5 values remain; the flow is identity |
| Exception Handling | two exceptions with the same message | identity | `Result` carries the criterion of the exception; a data class exception is content |

The distribution of the twenty-three lessons: singleton 5, identity 8, content 10, partial content 0. Partial content was not the criterion of any lesson; it appeared only in the bounding measurements. The core of the opening lesson is a separate count: the distribution of its eight values is 2/2/3/1 (singleton/identity/content/partial content). The two are not the same thing, because the core counts eight values and the table counts twenty-three lessons.

That two exceptions built with the same message do not come out equal gathers the whole answer of the course: in Kotlin, equality is given not by how a value looks but by the rule of its type. The next course is Android Platform and Tools; it covers the parts of an app and the life cycles of those parts.

