├── README.md
├── exercises
├── .gitignore
├── .idea
│ ├── codeStyles
│ │ ├── Project.xml
│ │ └── codeStyleConfig.xml
│ ├── encodings.xml
│ ├── kotlinc.xml
│ ├── markdown-navigator.xml
│ ├── markdown-navigator
│ │ └── profiles_settings.xml
│ └── misc.xml
├── build.gradle
├── gradle
│ └── wrapper
│ │ ├── gradle-wrapper.jar
│ │ └── gradle-wrapper.properties
├── gradlew
├── gradlew.bat
├── settings.gradle
└── src
│ └── main
│ └── kotlin
│ ├── chapter02
│ ├── Exercise2.1.kts
│ ├── Exercise2.10.kt
│ ├── Exercise2.11.kt
│ ├── Exercise2.2.kts
│ ├── Exercise2.2Bonus.kts
│ ├── Exercise2.3.kt
│ ├── Exercise2.4.kts
│ ├── Exercise2.5.kts
│ ├── Exercise2.6.kts
│ ├── Exercise2.7.kts
│ ├── Exercise2.8.kts
│ └── Exercise2.9.kts
│ ├── chapter03
│ ├── Exercise3.1.kts
│ ├── Exercise3.2.kts
│ ├── Exercise3.3.kts
│ ├── Exercise3.4.kts
│ ├── Exercise3.5.kts
│ ├── Exercise3.6.kts
│ ├── Exercise3.7.kts
│ ├── Exercise3.8.kts
│ └── Exercise3.9.kts
│ ├── chapter04
│ ├── exercise4_1
│ │ ├── Course.kt
│ │ ├── Grade.kt
│ │ ├── Main.kt
│ │ ├── Professor.kt
│ │ ├── Student.kt
│ │ └── University.kt
│ ├── exercise4_2
│ │ └── Exercise4.2.kt
│ ├── exercise4_3
│ │ └── Exercise4.3.kt
│ ├── exercise4_4
│ │ ├── Enemy.kt
│ │ ├── GameCharacter.kt
│ │ ├── Goblin.kt
│ │ ├── Hero.kt
│ │ ├── Main.kt
│ │ ├── Orc.kt
│ │ └── Warrior.kt
│ ├── exercise4_5
│ │ └── Exercise4.5.kts
│ ├── exercise4_6
│ │ └── Exercise4.6.kt
│ └── exercise4_7
│ │ └── Exercise4.7.kt
│ ├── chapter05
│ ├── exercise5_1
│ │ ├── Exercise5.1.kt
│ │ └── Exercise5_1.java
│ ├── exercise5_2
│ │ ├── Exercise5.2.kts
│ │ ├── TreasureChest.java
│ │ └── TreasureChestOriginal.java
│ ├── exercise5_3
│ │ ├── Exercise5.3.kt
│ │ └── Exercise5_3.java
│ ├── exercise5_4
│ │ └── Declarations.kt
│ ├── exercise5_5
│ │ └── Exercise5.5.kts
│ └── exercise5_6
│ │ ├── CallingClass.java
│ │ ├── Trees.kt
│ │ └── TreesOriginal.kt
│ ├── chapter06
│ ├── exercise6_1
│ │ ├── Exercise6.1.kts
│ │ └── Exercise6.1Bonus.kts
│ ├── exercise6_2
│ │ ├── Exercise6.2.kts
│ │ └── Exercise6.2Bonus.kts
│ ├── exercise6_3
│ │ └── Exercise6.3.kt
│ ├── exercise6_4
│ │ └── Exercise6.4.kt
│ └── exercise6_5
│ │ └── Exercise6.5.kts
│ ├── chapter07
│ └── chapter07.md
│ ├── chapter08
│ └── chapter08.md
│ └── chapter09
│ ├── exercise9_1
│ ├── Exercise9.1.kt
│ └── Main.kts
│ └── exercise9_2
│ └── Solution.md
└── listings
├── .gitignore
├── .idea
├── compiler.xml
├── encodings.xml
├── kotlinc.xml
├── listings.iml
├── markdown-navigator.xml
├── markdown-navigator
│ └── profiles_settings.xml
├── misc.xml
└── modules
│ ├── chapter02_main.iml
│ ├── chapter02_test.iml
│ ├── listings_main.iml
│ └── listings_test.iml
├── build.gradle
├── gradle
└── wrapper
│ ├── gradle-wrapper.jar
│ └── gradle-wrapper.properties
├── gradlew
├── gradlew.bat
├── settings.gradle
└── src
└── main
├── java
└── chapter05
│ ├── Box.java
│ ├── GettersAndSetters.java
│ ├── KeywordsAsIdentifiers.java
│ ├── Listing0503.java
│ ├── Listing0504.java
│ ├── Listing0506.java
│ ├── Listing0510.java
│ ├── Listing0511.java
│ ├── Listing0518.java
│ ├── Listing0519.java
│ ├── Listing0520.java
│ ├── Listing0521.java
│ ├── Listing0522.java
│ ├── Listing0526.java
│ ├── Listing0528.java
│ ├── Listing0529.java
│ ├── Listing0530.java
│ └── Producer.java
└── kotlin
├── chapter02
├── Listing0201.kts
├── Listing0202.kts
├── Listing0203.kts
├── Listing0204.kts
├── Listing0205.kts
├── Listing0206.kts
├── Listing0207.kts
├── Listing0208.kts
├── Listing0209.kts
├── Listing0210.kts
├── Listing0211.kts
├── Listing0212.kts
├── Listing0213.kts
├── Listing0214.kts
├── Listing0215.kts
├── Listing0216.kts
├── Listing0217.kts
├── Listing0218.kts
├── Listing0219.kt
├── Listing0220.kts
├── Listing0221.kts
├── Listing0222.kts
├── Listing0223.kts
├── Listing0224.kts
├── Listing0225.kt
├── Listing0226.kts
├── Listing0227.kts
├── Listing0228.kts
├── Listing0229.kts
├── Listing0230.kts
├── Listing0231.kts
├── Listing0232.kts
├── Listing0233.kts
├── Listing0234.kts
├── Listing0235.kts
├── Listing0236.kts
├── Listing0237.kts
├── Listing0238.kts
├── Listing0239.kts
└── Listing0240.kts
├── chapter03
├── Listing0301.kts
├── Listing0302.kts
├── Listing0303.kts
├── Listing0304.kts
├── Listing0305.kts
├── Listing0306.kts
├── Listing0307.kts
├── Listing0308.kts
├── Listing0309.kts
├── Listing0310.kts
├── Listing0311.kts
├── Listing0312.kts
├── Listing0313.kts
├── Listing0314.kts
├── Listing0315.kts
├── Listing0316.kts
├── Listing0317.kts
├── Listing0318.kts
├── Listing0319.kts
├── Listing0320.kts
├── Listing0321.kts
├── Listing0322.kts
├── Listing0323.kts
├── Listing0324.kts
├── Listing0325.kts
├── Listing0326.kts
├── Listing0327.kts
├── Listing0328.kts
├── Listing0329.kts
├── Listing0330.kts
├── Listing0331.kts
├── Listing0332.kts
├── Listing0333.kts
├── Listing0334.kts
├── Listing0335.kts
├── Listing0336.kts
├── Listing0337.kts
├── Listing0338.kts
├── Listing0339.kts
├── Listing0340.kts
├── Listing0341.kts
├── Listing0342.kts
├── Listing0343.kts
├── Listing0344.kts
├── Listing0345.kts
├── Listing0346.kts
├── Listing0347.kts
├── Listing0348.kts
├── Listing0349.kts
├── Listing0350.kts
├── Listing0351.kts
├── Listing0352.kts
├── Listing0353.kts
├── Listing0354.kts
├── Listing0355.kts
└── Listing0357.kts
├── chapter04
├── Listing0401.kts
├── Listing0402.kts
├── Listing0403.kts
├── Listing0404.kts
├── Listing0405.kts
├── Listing0406.kts
├── Listing0407.kts
├── Listing0408.kts
├── Listing0409.kts
├── Listing0410.kts
├── Listing0411.kts
├── Listing0412.kts
├── Listing0413.kts
├── Listing0414.kts
├── Listing0415.kts
├── Listing0416.kts
├── Listing0417.kts
├── Listing0418.kts
├── Listing0419.kts
├── Listing0420.kts
├── Listing0421.kts
├── Listing0422.kts
├── Listing0423.kts
├── Listing0424.kts
├── Listing0425.kts
├── Listing0426.kts
├── Listing0427.kts
├── Listing0428.kts
├── Listing0429.kts
├── Listing0430.kts
├── Listing0431.kt
├── Listing0432.kts
├── Listing0433.kts
├── Listing0434.kts
├── Listing0435.kts
├── Listing0436.kts
├── Listing0437.kts
├── Listing0438.kts
├── Listing0439.kts
├── Listing0440.kt
├── Listing0441.kt
├── Listing0442.kts
├── Listing0443.kts
├── Listing0444.kts
├── Listing0445.kts
├── Listing0446.kts
├── Listing0447.kts
├── Listing0448.kts
├── Listing0449.kts
├── Listing0450.kt
├── Listing0451.kts
├── Listing0452.kts
├── Listing0453.kts
├── Listing0454.kts
├── Listing0455.kts
├── Listing0456.kts
├── Listing0456JavaNote.java
├── Listing0457.kts
├── Listing0458.java
├── Listing0459.kts
├── Listing0460.kts
├── Listing0461.kts
├── Listing0462.kts
├── Listing0463.kts
├── Listing0464.kts
├── Listing0465.kts
├── Listing0466.kts
├── Listing0467.kts
├── Listing0468.kts
├── Listing0469.kts
├── Listing0470.kts
├── Listing0471.kts
├── Listing0472.kts
└── Listing0473.kts
├── chapter05
├── Listing0501.kts
├── Listing0502.kts
├── Listing0503.kts
├── Listing0504.kts
├── Listing0505.kts
├── Listing0506.kts
├── Listing0507.kts
├── Listing0508.kts
├── Listing0509.kt
├── Listing0510.kt
├── Listing0511.kt
├── Listing0512.kt
├── Listing0513.kt
├── Listing0514.kt
├── Listing0515.kt
├── Listing0516.kt
├── Listing0517.kt
├── Listing0518.kt
├── Listing0519.kt
├── Listing0520.kt
├── Listing0521.kt
├── Listing0522.kts
├── Listing0523.kt
├── Listing0524.kts
├── Listing0525.kt
├── Listing0526.kt
├── Listing0528.kt
├── Listing0529.kt
└── Listing0530.kt
├── chapter06
├── Listing0601.kts
├── Listing0602.kts
├── Listing0604.kts
├── Listing0605.kts
├── Listing0607.kts
├── Listing0608.kts
├── Listing0609.kts
├── Listing0610.kts
├── Listing0611.kts
├── Listing0612.kt
├── Listing0613.kts
├── Listing0615.kts
├── Listing0616.kts
├── Listing0617.kts
├── Listing0618.kts
├── Listing0619.kts
├── Listing0620.kts
├── Listing0621.kts
├── Listing0622.kts
├── Listing0623.kts
├── Listing0624.kts
├── Listing0625.kts
├── Listing0626.kts
├── Listing0627.kts
├── Listing0628.kts
├── Listing0629.kts
├── Listing0630.kts
├── Listing0631.kts
├── Listing0632.kts
├── Listing0633.kts
├── Listing0634.kts
├── Listing0635.kts
├── Listing0636.kts
├── Listing0637.kt
├── Listing0638.kt
├── Listing0639.kt
├── Listing0640.kts
├── Listing0642.kts
├── Listing0643.kts
├── Listing0644.kts
├── Listing0645.kts
├── Listing0646.kts
├── Listing0649.kts
├── Listing0650.kts
├── Listing0651.kts
├── Listing0652.kts
├── Listing0653.kts
├── Listing0654.kts
├── Listing0655.kts
├── Listing0655_Classes.kt
├── Listing0656.kts
├── Listing0657.kts
├── Listing0658.kts
├── Listing0660.kts
├── Listing0661.kt
└── Listing0662.kts
├── chapter09
├── Listing0901.kts
├── Listing0902.kts
├── Listing0903.kts
├── Listing0904.kts
├── Listing0905.kts
├── Listing0906.kts
├── Listing0907.kts
├── Listing0908.kts
├── Listing0909.kts
├── Listing0910.kts
├── Listing0911.kts
├── Listing0912.kts
├── Listing0913.kts
├── Listing0914.kts
├── Listing0915.kts
├── Listing0916.kts
├── Listing0917.kts
├── Listing0918.kts
├── Listing0919.kts
├── Listing0920.kts
├── Listing0921.kts
├── Listing0922.kts
├── Listing0923.kts
├── Listing0924.kts
├── Listing0925.kts
├── Listing0926.kt
├── Listing0927.kt
└── Listing0928-0941.md
└── chapter10
└── Listing10.1.kts
/exercises/.idea/codeStyles/Project.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
--------------------------------------------------------------------------------
/exercises/.idea/codeStyles/codeStyleConfig.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
--------------------------------------------------------------------------------
/exercises/.idea/encodings.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/exercises/.idea/kotlinc.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/exercises/.idea/markdown-navigator/profiles_settings.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
--------------------------------------------------------------------------------
/exercises/.idea/misc.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
--------------------------------------------------------------------------------
/exercises/build.gradle:
--------------------------------------------------------------------------------
1 | buildscript {
2 | ext.kotlin_version = '1.3.0'
3 |
4 | repositories {
5 | mavenCentral()
6 | }
7 | dependencies {
8 | classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
9 | }
10 | }
11 |
12 | group 'com.kotlinandroidbook'
13 | version '1.0-SNAPSHOT'
14 |
15 | apply plugin: 'kotlin'
16 |
17 | repositories {
18 | mavenCentral()
19 | }
20 |
21 | dependencies {
22 | compile "org.jetbrains.kotlin:kotlin-stdlib-jdk8:$kotlin_version"
23 | implementation "org.jetbrains.kotlinx:kotlinx-coroutines-core:1.0.0"
24 | implementation 'org.checkerframework:checker-qual:2.5.6'
25 | compile "org.jetbrains.kotlin:kotlin-script-runtime:1.3.0"
26 | }
27 |
28 | compileKotlin {
29 | kotlinOptions.jvmTarget = "1.8"
30 | }
31 | compileTestKotlin {
32 | kotlinOptions.jvmTarget = "1.8"
33 | }
--------------------------------------------------------------------------------
/exercises/gradle/wrapper/gradle-wrapper.jar:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/petersommerhoff/kotlin-for-android-app-development/2b3ffe6430ac6389da311f7600a57f766476d157/exercises/gradle/wrapper/gradle-wrapper.jar
--------------------------------------------------------------------------------
/exercises/gradle/wrapper/gradle-wrapper.properties:
--------------------------------------------------------------------------------
1 | #Tue Aug 28 15:08:45 CEST 2018
2 | distributionBase=GRADLE_USER_HOME
3 | distributionPath=wrapper/dists
4 | zipStoreBase=GRADLE_USER_HOME
5 | zipStorePath=wrapper/dists
6 | distributionUrl=https\://services.gradle.org/distributions/gradle-4.4-all.zip
7 |
--------------------------------------------------------------------------------
/exercises/settings.gradle:
--------------------------------------------------------------------------------
1 | rootProject.name = 'exercises'
2 |
3 |
--------------------------------------------------------------------------------
/exercises/src/main/kotlin/chapter02/Exercise2.1.kts:
--------------------------------------------------------------------------------
1 | package chapter02
2 |
3 | /**
4 | * @author Peter Sommerhoff
5 | */
6 |
7 | for (count in 1..6) {
8 | for (i in 1..count) {
9 | print("#")
10 | }
11 | println()
12 | }
--------------------------------------------------------------------------------
/exercises/src/main/kotlin/chapter02/Exercise2.10.kt:
--------------------------------------------------------------------------------
1 | package chapter02
2 |
3 | /**
4 | * @author Peter Sommerhoff
5 | */
6 |
7 | import kotlin.collections.*
8 |
9 | fun anagramsOfSubstrings(str: String): Int {
10 | var anagramCount = 0
11 | val lengthToSubstrings = getSubstringsByLength(str)
12 | for (substrings in lengthToSubstrings.values) {
13 | for ((i, s1) in substrings.withIndex()) {
14 | for (s2 in substrings.elementsAfter(i)) {
15 | if (s1.isAnagramOf(s2)) anagramCount++
16 | }
17 | }
18 | }
19 | return anagramCount
20 | }
21 |
22 | private fun getSubstringsByLength(str: String): Map> {
23 | val lengthToSubstrings = mutableMapOf>()
24 | for (length in 1..(str.length - 1)) {
25 | lengthToSubstrings[length] = str.windowed(size = length, step = 1)
26 | }
27 |
28 | return lengthToSubstrings
29 | }
30 |
31 | private fun List.elementsAfter(index: Int) = this.subList(index + 1, this.size)
32 |
33 | private fun String.isAnagramOf(other: String): Boolean = (letterFrequencies(this) == letterFrequencies(other))
34 |
35 | private fun letterFrequencies(str: String): Map =
36 | str.toCharArray().groupBy { it }.map { it.key to it.value.size }.toMap()
37 |
38 | fun main(args: Array) {
39 | println(anagramsOfSubstrings("abba")) // 4
40 | println(anagramsOfSubstrings("abcd")) // 0
41 | println(anagramsOfSubstrings("kkkk")) // 10
42 | println(anagramsOfSubstrings("abab")) // 5
43 | println(anagramsOfSubstrings("peter")) // 2
44 | println(anagramsOfSubstrings("heyyy")) // 4
45 | println(anagramsOfSubstrings("heyyyy")) // 10
46 | }
47 |
--------------------------------------------------------------------------------
/exercises/src/main/kotlin/chapter02/Exercise2.2.kts:
--------------------------------------------------------------------------------
1 | package chapter02
2 |
3 | /**
4 | * @author Peter Sommerhoff
5 | */
6 |
7 | // Reads and parses command-line argument, handles possible exceptions
8 | val rows = try {
9 | require(args[0].toInt() >= 3)
10 | args[0].toInt()
11 | } catch (e: IllegalArgumentException) {
12 | System.err.println("Please use a tree size of at least 3. Using default size of 5...")
13 | 5
14 | } catch (e: ArrayIndexOutOfBoundsException) {
15 | System.err.println("Please specify the tree size as command-line parameter. Using default size of 5...")
16 | 5
17 | } catch (e: NumberFormatException) {
18 | System.err.println("Command-line argument must be an integer. Using default size of 5...")
19 | 5
20 | }
21 |
22 | // Extracts relevant data
23 | val treeWidth = (rows * 2) - 1
24 | val treeMiddle = treeWidth / 2
25 |
26 | // Prints tree 'leaves'
27 | for (row in 0 until rows) {
28 | for (i in 1..treeMiddle - row) print(" ")
29 | for (i in 1..(2 * row + 1)) print("#")
30 | println()
31 | }
32 |
33 | // Prints tree trunk
34 | for (i in 1 until treeMiddle) print(" ")
35 | print("###")
36 |
--------------------------------------------------------------------------------
/exercises/src/main/kotlin/chapter02/Exercise2.3.kt:
--------------------------------------------------------------------------------
1 | package chapter02
2 |
3 | /**
4 | * @author Peter Sommerhoff
5 | */
6 |
7 | /*
8 |
9 | The Kotlin compiler will infer the following types for the expressions:
10 |
11 | rating: Double
12 | bugsPerLineOfCode: Float
13 | moleculeCount: Long
14 | QUIT_SYMBOL: Char
15 | ratio: Int (performs integer division)
16 | offset: Int
17 | punctuation: List
18 | status: String
19 | grades: IntRange
20 | lowerCaseLatin: CharRange
21 | fileLocation: String
22 | fileLocation: String?
23 | fileLocation: String
24 | hasAccess: Boolean (assuming there's a user object with an age property of type Int)
25 | usersToPurchases: Map>
26 | useless: Nothing?
27 |
28 | */
--------------------------------------------------------------------------------
/exercises/src/main/kotlin/chapter02/Exercise2.5.kts:
--------------------------------------------------------------------------------
1 | package chapter02
2 |
3 | /**
4 | * @author Peter Sommerhoff
5 | */
6 |
7 | // 1)
8 | val sentence = "Why are iPhone chargers not called apple juice?"
9 | val words = sentence.split(" ")
10 | var index = 0
11 | while (index < words.size) {
12 | print("${words[index]}. ")
13 | index++
14 | }
15 |
16 | // 2)
17 | val steps = 55..100 step 5
18 | index = 0 // Don't usually reuse the same index in another loop!
19 | while (true) {
20 | val gradeBoundary = steps.elementAtOrNull(index) ?: break
21 | println("Next grade: ${gradeBoundary - 5} - ${gradeBoundary}")
22 | index++
23 | }
24 |
25 | // 3)
26 | /*
27 | * This one cannot be expressed as a for loop in Kotlin.
28 | *
29 | * In contrast to languages like C++ and Java, while and for loops in Kotlin are not fundamentally
30 | * equivalent -- while loops are more expressive. More specifically, for loops in Kotlin are
31 | * equivalent to for-each loops from other languages and only allow iterating over anything that
32 | * provides an iterator().
33 | */
34 |
--------------------------------------------------------------------------------
/exercises/src/main/kotlin/chapter02/Exercise2.6.kts:
--------------------------------------------------------------------------------
1 | package chapter02
2 |
3 | import java.util.*
4 |
5 | /**
6 | * @author Peter Sommerhoff
7 | */
8 |
9 | val numberToGuess = Random().nextInt(100) + 1
10 | var userGuess = -1
11 | println("Guess the number between 1 and 100!")
12 |
13 | do {
14 | print("> ")
15 | userGuess = try {
16 | readLine()!!.toInt() // `readLine` cannot return null when using the command-line
17 | } catch (e: Exception) {
18 | println("Not a valid guess, please enter a number between 1 and 100")
19 | continue
20 | }
21 | when {
22 | userGuess < numberToGuess -> println("Too low!")
23 | userGuess == numberToGuess -> println("Congratulations, $userGuess is the correct number!")
24 | userGuess > numberToGuess -> println("Too high!")
25 | }
26 | } while (userGuess != numberToGuess)
--------------------------------------------------------------------------------
/exercises/src/main/kotlin/chapter02/Exercise2.7.kts:
--------------------------------------------------------------------------------
1 | package chapter02
2 |
3 | /**
4 | * @author Peter Sommerhoff
5 | */
6 |
7 | fun printMultiplicationTable(rows: IntRange, columns: IntRange) {
8 | for (row in rows) {
9 | for (col in columns) {
10 | System.out.format("%-8d", row * col) // Left-aligned output with 8 chars space per column
11 | }
12 | println()
13 | }
14 | }
15 |
16 | printMultiplicationTable(rows = 1..5, columns = 1..10)
--------------------------------------------------------------------------------
/exercises/src/main/kotlin/chapter02/Exercise2.8.kts:
--------------------------------------------------------------------------------
1 | package chapter02
2 |
3 | /**
4 | * @author Peter Sommerhoff
5 | */
6 |
7 | /*
8 | * - If one of the numbers is zero, the GCD is defined to be the other number
9 | * - Otherwise, you can find the GCD using recursion (calling the function inside itself)
10 | */
11 | fun greatestCommonDivisor(a: Int, b: Int): Int {
12 | if (a == 0) {
13 | return b
14 | }
15 | return greatestCommonDivisor(b % a, a)
16 | }
17 |
18 | /*
19 | * The implementation above can easily be translated into a single if-expression
20 | */
21 | fun greatestCommonDivisorShort(a: Int, b: Int): Int =
22 | if (a == 0) b else greatestCommonDivisorShort(b % a, a)
23 |
24 | /*
25 | * As a bonus sidenote for the interested, the implementation above is actually *tail-recursive*
26 | * because the only recursive call appears as the last statement inside the function body. Kotlin
27 | * allows you to mark such function as `tailrec` to let the compiler do additional optimizations.
28 | */
29 | tailrec fun greatestCommonDivisorTailrec(a: Int, b: Int): Int =
30 | if (a == 0) b else greatestCommonDivisorTailrec(b % a, a)
31 |
32 | /*
33 | * For two numbers a and b, it holds that gcd(a,b) * lcm(a,b) = a * b
34 | * Therefore, lcm(a,b) = a*b / gcd(a,b)
35 | */
36 | fun leastCommonMultiple(a: Int, b: Int): Long = a.toLong() * b / greatestCommonDivisor(a, b)
37 |
38 | /*
39 | * Sanity-checks the two functions using a few sample inputs (this is not a proper test!)
40 | */
41 | for (a in arrayOf(6, 8, 15, 27, 34, 120)) {
42 | for (b in arrayOf(2, 3, 9, 17)) {
43 | println("GCD($a, $b) = ${greatestCommonDivisorShort(a, b)}")
44 | println("LCM($a, $b) = ${leastCommonMultiple(a, b)}")
45 | }
46 | }
--------------------------------------------------------------------------------
/exercises/src/main/kotlin/chapter03/Exercise3.1.kts:
--------------------------------------------------------------------------------
1 | package chapter03
2 |
3 | import kotlin.math.pow
4 |
5 | /**
6 | * @author Peter Sommerhoff
7 | */
8 |
9 | fun iterate(repetitions: Int, func: (Double) -> Double): (Double) -> Double = { d: Double ->
10 | var result = d
11 | for (i in 1..repetitions) {
12 | result = func(result)
13 | }
14 | result // Note that this is not the return value of `iterate` -- it's the return value of the returned lambda
15 | }
16 |
17 | val plusThree = iterate(3) { it + 1.0 }
18 | println(plusThree(3.1415)) // Note machine precision issue with floating point numbers
19 |
20 | val toThePowerOf8 = iterate(3) { it.pow(2) }
21 | println(toThePowerOf8(2.0))
22 |
--------------------------------------------------------------------------------
/exercises/src/main/kotlin/chapter03/Exercise3.2.kts:
--------------------------------------------------------------------------------
1 | package chapter03
2 |
3 | /**
4 | * @author Peter Sommerhoff
5 | */
6 |
7 | val parentsToChildren = mapOf(
8 | "Susan" to listOf("Kevin", "Katie"),
9 | "Marcus" to listOf("Claire"),
10 | "Kate" to emptyList(),
11 | "Mike" to listOf("Jake", "Helen", "John")
12 | )
13 |
14 | val result = parentsToChildren.filter { it.value.isNotEmpty() }
15 | .filter { it.key.toUpperCase().startsWith("M") }
16 | .map { "${it.key}'s children are ${it.value.joinToString(" and ")}" }
17 |
18 | println(result)
--------------------------------------------------------------------------------
/exercises/src/main/kotlin/chapter03/Exercise3.5.kts:
--------------------------------------------------------------------------------
1 | package chapter03
2 |
3 |
4 | /**
5 | * @author Peter Sommerhoff
6 | */
7 |
8 | fun Int.isEven() = this % 2 == 0
9 |
10 | fun generateHailstoneSequence(initial: Int) =
11 | if (initial <= 0)
12 | throw IllegalArgumentException("Sequence must start with a positive integer")
13 | else generateSequence(initial) { n ->
14 | if (n == 1) return@generateSequence null // Makes the sequence terminate
15 | if (n.isEven()) n / 2 else 3 * n + 1 // Defines value of next element via lambda return value
16 | }
17 |
18 | fun buildHailstoneSequence(initial: Int) =
19 | if (initial <= 0)
20 | throw IllegalArgumentException("Sequence must start with a positive integer")
21 | else sequence {
22 | // Use `sequence` instead in Kotlin 1.3
23 | var next = initial
24 | while (true) {
25 | yield(next)
26 | if (next == 1) return@sequence
27 | next = if (next.isEven()) next / 2 else 3 * next + 1
28 | }
29 | }
30 |
31 | // Simple test runs for `generateSequence`
32 | val hailstoneOfSeven = generateHailstoneSequence(7)
33 | val hailstoneOf1337 = generateHailstoneSequence(1337)
34 |
35 | hailstoneOfSeven.forEach { print("$it, ") }
36 | println()
37 | hailstoneOf1337.forEach { print("$it, ") }
38 |
39 | println("\n")
40 |
41 | // Simple test runs for `buildSequence`
42 | val builtHailstoneOfSeven = buildHailstoneSequence(7)
43 | val builtHailstoneOf1337 = buildHailstoneSequence(1337)
44 |
45 | builtHailstoneOfSeven.forEach { print("$it, ") }
46 | println()
47 | builtHailstoneOf1337.forEach { print("$it, ") }
--------------------------------------------------------------------------------
/exercises/src/main/kotlin/chapter03/Exercise3.7.kts:
--------------------------------------------------------------------------------
1 | package chapter03
2 |
3 | /**
4 | * @author Peter Sommerhoff
5 | */
6 |
7 | // Usually, you would use the signature fun Iterable.map(f: (T) -> R) but this is covered in Chapter 4.
8 |
9 | // Uses the abstract interface Iterable so that it works with all collections, ranges and more
10 | fun Iterable.mapTo(transform: (Int) -> Int): List {
11 | val result = mutableListOf()
12 | for (element in this) {
13 | result.add(transform(element))
14 | }
15 |
16 | return result.toList()
17 | }
18 |
19 | // Simple test runs
20 | val numbers = listOf(2, 7, 6, 11)
21 | val mappedNumbers = numbers.mapTo { it * it }
22 | println(mappedNumbers)
23 |
24 | val range = 5..10
25 | val mappedRange = range.mapTo { it + 5 }
26 | println(mappedRange)
--------------------------------------------------------------------------------
/exercises/src/main/kotlin/chapter03/Exercise3.9.kts:
--------------------------------------------------------------------------------
1 | package chapter03
2 |
3 | /**
4 | * @author Peter Sommerhoff
5 | */
6 |
7 | /*
8 | * To generate these rational numbers, we cannot simply start going through 1, 2, 3, 4, ... because
9 | * we would never "arrive" at any non-integer numbers. A similar statement goes for any sequence
10 | * where only the denominator increases first.
11 | *
12 | * The idea is to generate the sequence of elements 1, 1/2, 2, 1/3, 2/3, 3, 1/4, 2/4, 3/4, 4, ...
13 | *
14 | * You can visualize this in an infinite triangle where we go through the bottom-left to top-right
15 | * diagonals:
16 | *
17 | * 1 2 3 4 5
18 | * 1/2 2/3 3/4 4/5
19 | * 1/3 2/4 3/5
20 | * 1/4 2/5
21 | * 1/5
22 | *
23 | * This ensures that every rational numbers leq 1 is theoretically generated at some point.
24 | */
25 |
26 |
27 |
28 | fun generateRationals(): Sequence {
29 | var denominator = 1
30 | var numerator = 1
31 | return sequence {
32 | while (true) {
33 | if (numerator == denominator) { // Switches to next diagonal
34 | yield(denominator++.toDouble()) // Yields denominator and increments it for next iteration via ++
35 | numerator = 1
36 | } else { // Iterates through current diagonal
37 | yield(numerator++.toDouble() / denominator) // Increments numerator for next iteration via ++
38 | }
39 | }
40 | }
41 | }
42 |
43 | // Simple test run
44 | val rationals = generateRationals()
45 | rationals.take(20).forEach(::println)
--------------------------------------------------------------------------------
/exercises/src/main/kotlin/chapter04/exercise4_1/Course.kt:
--------------------------------------------------------------------------------
1 | package chapter04.exercise4_1
2 |
3 | /**
4 | * @author Peter Sommerhoff
5 | */
6 | data class Course(val title: String, val description: String)
--------------------------------------------------------------------------------
/exercises/src/main/kotlin/chapter04/exercise4_1/Grade.kt:
--------------------------------------------------------------------------------
1 | package chapter04.exercise4_1
2 |
3 | /**
4 | * @author Peter Sommerhoff
5 | */
6 | enum class Grade {
7 | A, B, C, D, FAILED
8 | }
--------------------------------------------------------------------------------
/exercises/src/main/kotlin/chapter04/exercise4_1/Main.kt:
--------------------------------------------------------------------------------
1 | package chapter04.exercise4_1
2 |
3 | /**
4 | * @author Peter Sommerhoff
5 | */
6 |
7 | fun main(args: Array) {
8 | val rwth = University("RWTH Aachen University", 1870)
9 | val professor = Professor("John Doe", 44, 90_000)
10 | val student = Student("Sarah Keller", 22, 123456)
11 | val course = Course(
12 | "Programming I",
13 | "Learn object-oriented, functional, and logical programming"
14 | )
15 |
16 | rwth.hire(professor)
17 | rwth.enroll(student)
18 | professor.teach(course)
19 | student.enroll(course)
20 | student.party()
21 | student.party()
22 | student.party()
23 | student.learn()
24 | student.takeExam(course)
25 | professor.doOralExam(student, course)
26 | }
--------------------------------------------------------------------------------
/exercises/src/main/kotlin/chapter04/exercise4_1/Professor.kt:
--------------------------------------------------------------------------------
1 | package chapter04.exercise4_1
2 |
3 | /**
4 | * @author Peter Sommerhoff
5 | */
6 | class Professor(val name: String, val age: Int, val yearlySalary: Int) {
7 |
8 | val coursesTaught = mutableListOf()
9 |
10 | fun teach(course: Course) {
11 | coursesTaught.add(course)
12 | println("Teaching ${course.title}...")
13 | }
14 |
15 | fun doOralExam(student: Student, course: Course) {
16 | println("Testing student ${student.name} in ${course.title}")
17 | student.grade(course, Grade.A) // Everyone gets an A
18 | }
19 | }
--------------------------------------------------------------------------------
/exercises/src/main/kotlin/chapter04/exercise4_1/Student.kt:
--------------------------------------------------------------------------------
1 | package chapter04.exercise4_1
2 |
3 | import kotlin.math.max
4 | import kotlin.math.min
5 |
6 | /**
7 | * @author Peter Sommerhoff
8 | */
9 | class Student(val name: String, val age: Int, val matriculationNo: Int) {
10 |
11 | val grades = mutableMapOf()
12 | val currentEnrollments = mutableListOf()
13 |
14 | private var successLikelihoodInPercent: Int = 50
15 |
16 | fun enroll(course: Course) {
17 | currentEnrollments.add(course)
18 | println("$name enrolled in ${course.title}...")
19 | }
20 |
21 | fun takeExam(course: Course) {
22 | println("$name takes exam in ${course.title}...")
23 | }
24 |
25 | fun learn() {
26 | println("$name is learning...")
27 | successLikelihoodInPercent = min(successLikelihoodInPercent + 2, 100)
28 | }
29 |
30 | fun party() {
31 | println("$name is partying! \uD83C\uDF89")
32 | successLikelihoodInPercent = max(successLikelihoodInPercent - 2, 0)
33 | }
34 |
35 | fun grade(course: Course, grade: Grade) {
36 | grades[course] = grade
37 | }
38 | }
--------------------------------------------------------------------------------
/exercises/src/main/kotlin/chapter04/exercise4_1/University.kt:
--------------------------------------------------------------------------------
1 | package chapter04.exercise4_1
2 |
3 | /**
4 | * @author Peter Sommerhoff
5 | */
6 | class University(val name: String, val foundingYear: Int) {
7 |
8 | val professors = mutableListOf()
9 | val students = mutableListOf()
10 |
11 | fun hire(prof: Professor) {
12 | professors.add(prof)
13 | println("Hired Professor ${prof.name} at $name...")
14 | }
15 |
16 | fun enroll(newStudent: Student) {
17 | students.add(newStudent)
18 | println("Enrolled student ${newStudent.name} at $name...")
19 | }
20 | }
--------------------------------------------------------------------------------
/exercises/src/main/kotlin/chapter04/exercise4_2/Exercise4.2.kt:
--------------------------------------------------------------------------------
1 | package chapter04.exercise4_2
2 |
3 | /**
4 | * @author Peter Sommerhoff
5 | */
6 |
7 | /*
8 | * Although a square "is-a" rectangle, it requires additional invariants, here width == height.
9 | * In such scenarios, inheritance may be undesirable. It's useful rather when a subclass really
10 | * just adds functionality in addition to its superclass.
11 | *
12 | * The problem here boils down to the Liskov Substitution Principle (LSP), one of the principles
13 | * of object-oriented software design. It states that a subclass should always be able to be used
14 | * in place of its superclass and behave the same way (preserve invariants etc).
15 | *
16 | * The takeaway is that object-oriented design, and especially inheritance, are not always the ideal
17 | * way to model or solve a problem.
18 | */
19 | open class Rectangle(var width: Double, var height: Double) {
20 | open fun area() = width * height
21 | open fun stretch(factor: Double) {
22 | width *= factor
23 | }
24 | }
25 |
26 | class Square(width: Double) : Rectangle(width, width) {
27 | override fun stretch(factor: Double) {
28 | super.stretch(factor)
29 | height *= factor // Violates invariant of Rectangle. But otherwise would violate invariant of Square.
30 | }
31 | }
32 |
33 | fun main(args: Array) {
34 | val rect: Rectangle = Square(10.0) // Rectangle happens to be a Square at runtime
35 | rect.stretch(2.0)
36 | println(rect.height) // Would expect unchanged 10.0 for a rectangle
37 | println(rect.width) // Would expect stretched 20.0 for a rectangle
38 | println(rect.area()) // Would expect 200.0 for a rectangle that was stretched only in width
39 | }
--------------------------------------------------------------------------------
/exercises/src/main/kotlin/chapter04/exercise4_4/Enemy.kt:
--------------------------------------------------------------------------------
1 | package chapter04.exercise4_4
2 |
3 | /**
4 | * @author Peter Sommerhoff
5 | */
6 | abstract class Enemy(name: String, hp: Int, attackPoints: Int, defensePoints: Int, val experiencePoints: Int)
7 | : GameCharacter(name, hp, attackPoints, defensePoints)
--------------------------------------------------------------------------------
/exercises/src/main/kotlin/chapter04/exercise4_4/GameCharacter.kt:
--------------------------------------------------------------------------------
1 | package chapter04.exercise4_4
2 |
3 | import kotlin.math.max
4 |
5 | /**
6 | * @author Peter Sommerhoff
7 | */
8 | abstract class GameCharacter(val name: String, var healthPoints: Int, var attackPoints: Int, var defensePoints: Int) {
9 |
10 | fun attack(other: GameCharacter) {
11 | val damage = max(0, this.attackPoints - other.defensePoints)
12 | other.healthPoints = max(0, other.healthPoints - damage)
13 | println("> ${this.name} made $damage damage to ${other.name} (${other.name} has ${other.healthPoints} HP left)")
14 | }
15 |
16 | fun isDead() = healthPoints <= 0
17 |
18 | fun heal(hp: Int) {
19 | this.healthPoints += hp // There's no maximum for HP
20 | println("> ${this.name} healed $hp HP (now has ${this.healthPoints} HP)")
21 | }
22 | }
--------------------------------------------------------------------------------
/exercises/src/main/kotlin/chapter04/exercise4_4/Goblin.kt:
--------------------------------------------------------------------------------
1 | package chapter04.exercise4_4
2 |
3 | /**
4 | * @author Peter Sommerhoff
5 | */
6 | class Goblin : Enemy("Goblin", 16, 9, 3, 75)
--------------------------------------------------------------------------------
/exercises/src/main/kotlin/chapter04/exercise4_4/Hero.kt:
--------------------------------------------------------------------------------
1 | package chapter04.exercise4_4
2 |
3 | /**
4 | * @author Peter Sommerhoff
5 | */
6 | abstract class Hero(
7 | name: String,
8 | hp: Int,
9 | attackPoints: Int,
10 | defensePoints: Int,
11 | var hasSword: Boolean = false,
12 | var hasArmor: Boolean = false,
13 | var experiencePointsEarned: Int = 0
14 | ) : GameCharacter(name, hp, attackPoints, defensePoints) {
15 |
16 | private val swordAttackBonus = 6
17 | private val armorDefenseBonus = 2
18 |
19 | fun equipSword() {
20 | if (hasSword) return
21 |
22 | attackPoints += swordAttackBonus
23 | hasSword = true
24 | println("> You found a sword! You now have ${this.attackPoints} ATK!")
25 | }
26 |
27 | fun equipArmor() {
28 | if (hasArmor) return
29 |
30 | defensePoints += armorDefenseBonus
31 | hasArmor = true
32 | println("> You found an armor! You now have ${this.defensePoints} DEF!")
33 | }
34 |
35 | fun fight(enemy: Enemy) {
36 | while (this.healthPoints > 0 && enemy.healthPoints > 0) {
37 | attack(enemy)
38 | if (!enemy.isDead()) {
39 | enemy.attack(this)
40 | }
41 | }
42 | if (!isDead()) {
43 | experiencePointsEarned += enemy.experiencePoints
44 | }
45 | }
46 | }
--------------------------------------------------------------------------------
/exercises/src/main/kotlin/chapter04/exercise4_4/Main.kt:
--------------------------------------------------------------------------------
1 | package chapter04.exercise4_4
2 |
3 | /**
4 | * @author Peter Sommerhoff
5 | */
6 | fun main(args: Array) {
7 |
8 | val hero = Warrior()
9 |
10 | loop@ while (true) { // Main game loop
11 | println("> Fight [g]oblin or [o]rc?")
12 | print("> ")
13 | val input = readLine()!!.toLowerCase()
14 | when (input) {
15 | !in arrayOf("g", "o") -> {
16 | println("> Please enter 'g' to fight a [g]oblin or 'o' to fight an [o]rc!")
17 | continue@loop
18 | }
19 | "g" -> hero.fight(Goblin())
20 | "o" -> hero.fight(Orc())
21 | }
22 |
23 | if (!hero.hasSword && Math.random() < 0.25)
24 | hero.equipSword()
25 |
26 | if (!hero.hasArmor && Math.random() < 0.15)
27 | hero.equipArmor()
28 |
29 | if (Math.random() < 0.10)
30 | hero.heal(10)
31 |
32 | if (hero.isDead()) {
33 | println(">>> You're dead! You earned ${hero.experiencePointsEarned} XP!")
34 | break
35 | } else {
36 | println("> Status: ${hero.healthPoints} HP left (${hero.attackPoints} ATK, ${hero.defensePoints} DEF)")
37 | }
38 | }
39 | }
--------------------------------------------------------------------------------
/exercises/src/main/kotlin/chapter04/exercise4_4/Orc.kt:
--------------------------------------------------------------------------------
1 | package chapter04.exercise4_4
2 |
3 | /**
4 | * @author Peter Sommerhoff
5 | */
6 | class Orc : Enemy("Orc", 30, 11, 4, 200)
--------------------------------------------------------------------------------
/exercises/src/main/kotlin/chapter04/exercise4_4/Warrior.kt:
--------------------------------------------------------------------------------
1 | package chapter04.exercise4_4
2 |
3 | /**
4 | * @author Peter Sommerhoff
5 | */
6 | class Warrior : Hero("Warrior", 50, 10, 5)
--------------------------------------------------------------------------------
/exercises/src/main/kotlin/chapter05/exercise5_1/Exercise5.1.kt:
--------------------------------------------------------------------------------
1 | package chapter05.exercise5_1
2 |
3 | import chapter05.exercise5_1.Exercise5_1.*
4 |
5 | /**
6 | * @author Peter Sommerhoff
7 | */
8 |
9 | /*
10 | * The inferred platform types are, in order, the following:
11 | * 1) Double
12 | * 2) Int!
13 | * 3) (Mutable)List
14 | * 4) CharArray!
15 | * 5) Array<(out) Person!>!
16 | * 6) Any!
17 | * 7) MutableList!>!
18 | * 8) (Mutable)Map>!
19 | */
20 |
21 | /*
22 | * Here are the examples in code to verify. These use the Java class `Exercise5_1`.
23 | * To see the inferred platform types, let IntelliJ show autocomplete inside the methods and look at the return types.
24 | */
25 | val average = calculateAverage() // Double
26 | val age = getAge() // Int!
27 | val titles = titles() // (Mutable)List
28 | val chars = toCharArray() // CharArray!
29 | val people = getAttendees() // Array<(out) Person!>!
30 | val thing = get() // Any!
31 | val ratings = fetchRatings() // MutableList!>!
32 | val locationsToEmployees = locationsToEmployees() // (Mutable)Map>!
--------------------------------------------------------------------------------
/exercises/src/main/kotlin/chapter05/exercise5_1/Exercise5_1.java:
--------------------------------------------------------------------------------
1 | package chapter05.exercise5_1;
2 |
3 | import org.checkerframework.checker.nullness.qual.NonNull;
4 |
5 | import java.util.List;
6 | import java.util.Map;
7 |
8 | /**
9 | * @author Peter Sommerhoff
10 | */
11 | public class Exercise5_1 {
12 |
13 | public static double calculateAverage() { return 1.0; } // Only method that cannot return null
14 |
15 | public static Integer getAge() { return null; }
16 |
17 | public static @NonNull
18 | List titles() { return null; } // Can still return null, but with a warning
19 |
20 | public static char[] toCharArray() { return null; }
21 |
22 | public static Person[] getAttendees() { return null; }
23 |
24 | public static Object get() { return null; }
25 |
26 | public static List extends Comparable> fetchRatings() { return null; }
27 |
28 | public static Map<@NonNull Address, @NonNull List> locationsToEmployees() { return null; }
29 |
30 | public class Address {}
31 | public class Employee {}
32 | public class Person {}
33 | }
34 |
--------------------------------------------------------------------------------
/exercises/src/main/kotlin/chapter05/exercise5_2/Exercise5.2.kts:
--------------------------------------------------------------------------------
1 | package chapter05.exercise5_2
2 |
3 | /**
4 | * @author Peter Sommerhoff
5 | */
6 |
7 | // Using the original class
8 | private val chestOriginal = TreasureChestOriginal()
9 | if (chestOriginal.`is`(TreasureChestOriginal().Diamond())) { // Must escape `is` because it's a hard keyword
10 | chestOriginal.open()
11 | println(chestOriginal.`object`) // Must escape `object` because it's a hard keyword
12 | chestOriginal.seal()
13 | }
14 |
15 | // Using the improved class
16 | private val chest = TreasureChest()
17 | if (chest.contains(TreasureChest().Diamond())) {
18 | chest.open()
19 | println(chest.treasure)
20 | chest.seal()
21 | }
--------------------------------------------------------------------------------
/exercises/src/main/kotlin/chapter05/exercise5_2/TreasureChest.java:
--------------------------------------------------------------------------------
1 | package chapter05.exercise5_2;
2 |
3 | import static chapter05.exercise5_2.TreasureChest.Status.OPEN;
4 | import static chapter05.exercise5_2.TreasureChest.Status.SEALED;
5 |
6 | /**
7 | * @author Peter Sommerhoff
8 | */
9 | class TreasureChest {
10 |
11 | // You don't have to use an enum here but it's good practice to avoid Kotlin keywords as field or method names.
12 | enum Status { OPEN, SEALED }
13 |
14 | private Status status = SEALED;
15 |
16 | // Here, `object` was a hard keyword so it's definitely better to avoid it
17 | Object treasure = new Diamond();
18 |
19 | // Here we avoid the hard keyword `is`; `contains` is a more conventional for this anyway
20 | boolean contains(Object object) { return this.treasure == object; }
21 |
22 | // `open` is a modifier in Kotlin but it's a good method name and unlikely to cause confusion so we kept it here.
23 | void open() { this.status = OPEN; }
24 | void seal() { this.status = SEALED; }
25 |
26 | // This avoids the soft keyword `get` as there's no need for it, it's a bad method name anyway
27 | Status status() { return this.status; }
28 |
29 | class Diamond {}
30 | }
31 |
--------------------------------------------------------------------------------
/exercises/src/main/kotlin/chapter05/exercise5_2/TreasureChestOriginal.java:
--------------------------------------------------------------------------------
1 | package chapter05.exercise5_2;
2 |
3 | /**
4 | * @author Peter Sommerhoff
5 | */
6 | class TreasureChestOriginal {
7 | public static final String sealed = "SEALED";
8 | public static final String open = "OPEN";
9 | String status = sealed;
10 | Object object = new Diamond();
11 |
12 | boolean is(Object object) { return this.object == object; }
13 | void open() { this.status = open; }
14 | void seal() { this.status = sealed; }
15 | String get() { return this.status; }
16 |
17 | class Diamond {}
18 | }
19 |
--------------------------------------------------------------------------------
/exercises/src/main/kotlin/chapter05/exercise5_3/Exercise5.3.kt:
--------------------------------------------------------------------------------
1 | @file:JvmName("JvmAnnotations")
2 |
3 | package chapter05.exercise5_3
4 |
5 | /**
6 | * @author Peter Sommerhoff
7 | */
8 |
9 | /*
10 | * 1) You want to prevent the generation of getters and setters for a property and instead allow direct field access.
11 | * -> @JvmField exposes a property directly as a field in the Java bytecode, thus without getters or setters.
12 | */
13 | class ExposedField {
14 | @JvmField val exposed = "No getter or setter"
15 | }
16 |
17 | /*
18 | * 2) You want to prevent class names such as CandyKt or CookieKt when using your Kotlin files from Java.
19 | * -> Any renaming of identifiers when compiling to Java bytecode can be done via @JvmName. To rename the class
20 | * generated from an entire file as in this scenario is done using @file:JvmName at the top of the file.
21 | */
22 | // See top of this file for an example
23 |
24 | /*
25 | * 3) You want to be able to call a companion object method directly on its containing class from Java.
26 | * -> Calling a method directly on a Java class is enabled by static methods so you need the @JvmStatic annotation.
27 | */
28 | class Accessor {
29 | companion object {
30 | @JvmStatic fun access() {}
31 | }
32 | }
33 |
34 | /*
35 | * 4) You want to at least have some of the overloaded options to call a Kotlin function with optional arguments from Java.
36 | * -> This is enabled by adding @JvmOverloads to the function declaration. It only enabled a constrained form of
37 | * optional arguments because the order of parameters is preserved -- for n optional parameters, only n+1 different
38 | * combinations of calling the method from Java are enabled.
39 | */
40 | @JvmOverloads fun overloaded(i: Int = 1, s: String = "", d: Double = 0.0) {}
--------------------------------------------------------------------------------
/exercises/src/main/kotlin/chapter05/exercise5_3/Exercise5_3.java:
--------------------------------------------------------------------------------
1 | package chapter05.exercise5_3;
2 |
3 | /**
4 | * @author Peter Sommerhoff
5 | */
6 | public class Exercise5_3 {
7 |
8 | public static void main(String[] args) {
9 |
10 | String field = new ExposedField().exposed;
11 |
12 | Accessor.access();
13 |
14 | JvmAnnotations.overloaded();
15 | JvmAnnotations.overloaded(42);
16 | JvmAnnotations.overloaded(42, "Hello");
17 | JvmAnnotations.overloaded(42, "Hello", 3.14159);
18 | }
19 | }
20 |
--------------------------------------------------------------------------------
/exercises/src/main/kotlin/chapter05/exercise5_4/Declarations.kt:
--------------------------------------------------------------------------------
1 | package chapter05.exercise5_4
2 |
3 | import javafx.scene.control.Button
4 | import java.math.BigDecimal
5 |
6 | /**
7 | * @author Peter Sommerhoff
8 | */
9 |
10 | /*
11 | * Generates no static members.
12 | */
13 | class Constants1 {
14 | @JvmField val PI = BigDecimal(3.1415926535897932384626433)
15 | }
16 |
17 | /*
18 | * Generates a static method due to @JvmStatic.
19 | * Additional static INSTANCE generated for Singleton pattern
20 | */
21 | object MainPresenter {
22 | fun notifyUser(message: String) {}
23 | }
24 |
25 | /*
26 | * Generates a private static member for `defaultRobot` but the public getter for it is not static.
27 | * Additional static member INSTANCE generated for Singleton pattern.
28 | */
29 | object RobotFactory {
30 | val defaultRobot = Robot()
31 | }
32 |
33 | /*
34 | * Generates a static field PI.
35 | * Additional static INSTANCE generated for Singleton pattern
36 | */
37 | object Constants2 {
38 | const val PI = 3.14159265358979323
39 | }
40 |
41 | /*
42 | * Generates a static nested class PersonBuilder.
43 | * The companion object methods are not static.
44 | */
45 | class Person {
46 | companion object PersonBuilder {
47 | fun build(): Person = Person()
48 | }
49 | }
50 |
51 | /*
52 | * Generates no static members.
53 | */
54 | class MainUi {
55 | lateinit var btnOk: Button
56 | }
57 |
58 |
59 |
60 |
61 | // -----------
62 | class Robot
--------------------------------------------------------------------------------
/exercises/src/main/kotlin/chapter05/exercise5_5/Exercise5.5.kts:
--------------------------------------------------------------------------------
1 | package chapter05.exercise5_5
2 |
3 | /**
4 | * @author Peter Sommerhoff
5 | */
6 |
7 | /*
8 | * A good return type for functions such as `fail` and `exit` is Kotlin's special `Nothing` type.
9 | */
10 | fun fail(message: String): Nothing = throw IllegalStateException(message)
11 |
12 | /*
13 | * This return type tells the compiler that the function never terminates, and the compiler can use this information to
14 | * infer interesting data such as nullability.
15 | */
16 | fun fetchMessage(): String? = "Hello, World!"
17 | val nullable: String? = fetchMessage()
18 | val notNullable: String = fetchMessage() ?: fail("Could not fetch a message.")
19 | // `notNullable` cannot be null here because, if it was, the line above wouldn't terminate so this line wouldn't execute
--------------------------------------------------------------------------------
/exercises/src/main/kotlin/chapter05/exercise5_6/CallingClass.java:
--------------------------------------------------------------------------------
1 | package chapter05.exercise5_6;
2 |
3 | import java.util.Arrays;
4 | import java.util.List;
5 |
6 | /**
7 | * @author Peter Sommerhoff
8 | */
9 | public class CallingClass {
10 |
11 | static void useTrees() {
12 | List> leafs = Arrays.asList(
13 | new Leaf<>(2),
14 | new Leaf<>(3)
15 | );
16 | Tree tree = new Branch<>(1, leafs);
17 |
18 | // "Original" method
19 | // Note that the IDE cannot infer the generic type in the argument until the second one is passed in
20 | TreesOriginalKt.applyToAllNodes(node -> node * node, tree);
21 |
22 | // Improved `map` method
23 | TreeUtils.map(tree, integer -> integer * integer);
24 |
25 | System.out.println(tree); // Uses Branch.toString() from data class
26 | }
27 |
28 | public static void main(String[] args) {
29 | useTrees();
30 | }
31 | }
32 |
--------------------------------------------------------------------------------
/exercises/src/main/kotlin/chapter05/exercise5_6/Trees.kt:
--------------------------------------------------------------------------------
1 | /*
2 | * - Giving the generated class an explicit name prevents ugly "TreesKt" naming and avoids leaking
3 | * the implementation to the outside.
4 | */
5 | @file:JvmName("TreeUtils")
6 |
7 | package chapter05.exercise5_6
8 |
9 | /**
10 | * @author Peter Sommerhoff
11 | */
12 |
13 | /*
14 | * - Extension function allows calling more naturally (from Kotlin)
15 | * - The concept of this function is that of a `map` function. Calling it `map` thus gets across its
16 | * functionality effectively and with a concise name (assuming readers know functional programming)
17 | * - Making the lambda the last parameter allows calling it without parentheses (from Kotlin)
18 | *
19 | * (Making the Tree immutable is definitely another way to improve the code but not assumed as part
20 | * of the solution here).
21 | */
22 | fun Tree.map(transform: (T) -> T) {
23 | when (this) {
24 | is Leaf -> value = transform(value)
25 | is Branch -> {
26 | value = transform(value)
27 | children.forEach { subtree -> subtree.map(transform) }
28 | }
29 | }
30 | }
--------------------------------------------------------------------------------
/exercises/src/main/kotlin/chapter05/exercise5_6/TreesOriginal.kt:
--------------------------------------------------------------------------------
1 | package chapter05.exercise5_6
2 |
3 | /**
4 | * @author Peter Sommerhoff
5 | */
6 |
7 | // Assume this file had the file name "Trees.kt"
8 |
9 | // Elements in this tree may change but its structure remains.
10 | // Modifying this class is not part of the exercise but feel free to do so.
11 | sealed class Tree
12 |
13 | data class Leaf(var value: T) : Tree()
14 | data class Branch(var value: T, val children: List>) : Tree() {
15 | init {
16 | require(children.isNotEmpty())
17 | }
18 | }
19 |
20 | fun applyToAllNodes(transform: (T) -> T, tree: Tree) {
21 | when (tree) {
22 | is Leaf -> tree.value = transform(tree.value) // Modifies the tree in-place (mutable)
23 | is Branch -> {
24 | tree.value = transform(tree.value)
25 | tree.children.forEach { subtree -> applyToAllNodes(transform, subtree) }
26 | }
27 | }
28 | }
--------------------------------------------------------------------------------
/exercises/src/main/kotlin/chapter06/exercise6_1/Exercise6.1Bonus.kts:
--------------------------------------------------------------------------------
1 | package chapter06.exercise6_1
2 |
3 | import kotlinx.coroutines.launch
4 | import kotlinx.coroutines.runBlocking
5 | import kotlinx.coroutines.sync.Mutex
6 | import kotlinx.coroutines.sync.withLock
7 |
8 | /**
9 | * @author Peter Sommerhoff
10 | */
11 |
12 | /*
13 | * These exercise solutions use Kotlin 1.3 along with a newer coroutines version that uses structured concurrency -- a
14 | * fascinating concept that makes your concurrent code more predictable and easier to reason about. It's used since
15 | * version 0.26.0: https://github.com/Kotlin/kotlinx.coroutines/releases/tag/0.26.0
16 | *
17 | * Additionally, the syntax change a little. Read about the changes here:
18 | * 1) Structured concurrency: https://medium.com/@elizarov/structured-concurrency-722d765aa952
19 | * 2) Kotlin 1.3: https://kotlinlang.org/docs/reference/whatsnew13.html
20 | */
21 |
22 | runBlocking {
23 |
24 | var counter = 0
25 | val lock = Mutex() // A mutex is the simplest way to implement mutual exclusion
26 |
27 | val coroutines = List(3) {
28 | launch {
29 | repeat(1_000_000) {
30 | lock.withLock { // Only one coroutine at a time can enter this block to increment the counter
31 | counter++
32 | }
33 | }
34 | }
35 | }
36 |
37 | coroutines.forEach { it.join() }
38 |
39 | println("Final counter: $counter")
40 | }
--------------------------------------------------------------------------------
/exercises/src/main/kotlin/chapter06/exercise6_2/Exercise6.2.kts:
--------------------------------------------------------------------------------
1 | package chapter06.exercise6_2
2 |
3 | import kotlinx.coroutines.coroutineScope
4 | import kotlinx.coroutines.launch
5 | import kotlinx.coroutines.runBlocking
6 | import java.net.URL
7 |
8 | /**
9 | * @author Peter Sommerhoff
10 | */
11 |
12 | /*
13 | * These exercise solutions use Kotlin 1.3 along with a newer coroutines version that uses structured concurrency -- a
14 | * fascinating concept that makes your concurrent code more predictable and easier to reason about. It's used since
15 | * version 0.26.0: https://github.com/Kotlin/kotlinx.coroutines/releases/tag/0.26.0
16 | *
17 | * Additionally, the syntax change a little. Read about the changes here:
18 | * 1) Structured concurrency: https://medium.com/@elizarov/structured-concurrency-722d765aa952
19 | * 2) Kotlin 1.3: https://kotlinlang.org/docs/reference/whatsnew13.html
20 | */
21 |
22 | private val API_URL = "http://api.icndb.com/jokes/random"
23 | val randomJokeUrl = URL(API_URL)
24 |
25 | runBlocking {
26 |
27 | println(fetchJoke())
28 |
29 | do {
30 | print("> Hit ENTER for 3 more jokes or 'q' to quit")
31 | val input = readLine()!!
32 | if (input.isBlank()) {
33 | fetchJokesParallel(count = 3)
34 | }
35 | } while(input != "q")
36 | }
37 |
38 | suspend fun fetchJokesParallel(count: Int) = coroutineScope {
39 | repeat(count) {
40 | launch {
41 | val joke = fetchJoke()
42 | println(joke)
43 | }
44 | }
45 | }
46 |
47 | fun fetchJoke(): String {
48 | val json = randomJokeUrl.readText()
49 | val regex = Regex("\"joke\": \"(.*)\",")
50 | val joke = regex.find(json)?.groupValues?.get(1) ?: "" // Returns the match from the first parentheses
51 | return joke
52 | }
--------------------------------------------------------------------------------
/exercises/src/main/kotlin/chapter07/chapter07.md:
--------------------------------------------------------------------------------
1 | # Exercises for Chapter 7
2 |
3 | There are no exercises for Chapter 7 in the Instructor's Manual but you
4 | can of course improve your Kudoo to-do app.
5 |
6 | Here are some ideas for inspiration:
7 | * Let users add an optional description text to their to-do items
8 | * Let users add a priority to their to-do items
9 | * Enable users to sort to-dos by priority inside MainActivity
10 | * Allow users to delete a to-do by swiping the item
11 | * Instead of deleting to-dos right away, archive them first and give users the option to delete them
12 |
13 | Of course, implementing your own app idea is another fantastic option!
14 |
15 | ---
16 | In Chapter 8, you'll implement a larger app to gain some more experience with Kotlin on Android. If you tried the ideas above but struggled with them, maybe try again after going through Chapter 8.
--------------------------------------------------------------------------------
/exercises/src/main/kotlin/chapter08/chapter08.md:
--------------------------------------------------------------------------------
1 | # Exercises for Chapter 8
2 |
3 | There are no exercises for Chapter 8 in the Instructor's Manual either but you can again improve the Nutrilicious app.
4 |
5 | How about fleshing out the app into an actual nutrition tracking app?
6 | * Let users enter the amounts they ate of each food
7 | * Show users the percentages of each nutrient they ate so far that day
8 | * If you want to be better than even popular tracking apps, give users recommendations for foods they should eat during the rest of the day to best cover their nutrient needs (and consider selling your app in the app store :smiley:)
9 | * Let users set personalized targets for each nutrient, for instance depending on their gender, weight, height, age, and activity level
10 |
11 | And again, implementing your own app idea is another fantastic option!
12 |
--------------------------------------------------------------------------------
/exercises/src/main/kotlin/chapter09/exercise9_1/Main.kts:
--------------------------------------------------------------------------------
1 | package chapter09.exercise9_1
2 |
3 | /**
4 | * @author Peter Sommerhoff
5 | */
6 |
7 | /*
8 | * There are a million different ways to design this DSL. One extremely useful feature here is to have extension
9 | * properties for the units (such as mg and g) and infix function `of` that allows the code to read almost like a recipe.
10 | *
11 | * There are certainly many ways to flesh this DSL out further, and I encourage you to do so if you want to gather more
12 | * experience building DSLs in Kotlin.
13 | */
14 |
15 | val bananaBreadRecipe = recipe("Banana & Walnut Bread") {
16 |
17 | description = "A healthy & tasty banana and walnut bread"
18 |
19 | add(2.pc of "Ripe banana")
20 | add(125.ml of "Milk (any kind)")
21 | add(10.ml of "Lemon juice")
22 |
23 | add(200.g of "All-purpose flour")
24 | add(100.g of "Whole wheat flour")
25 | add(100.g of "Brown sugar")
26 | add(1.pack of "Baking powder")
27 | add(2.g of "Salt")
28 | add(20.g of "Flaxseeds")
29 | add(100.g of "Walnuts")
30 |
31 | instructions = """Mash bananas and mix with rest of wet ingredients. Mix all dry ingredients and stir into
32 | wet ingredients until you get a (quite wet) dough. Put into baking form, sprinkle water on top,
33 | and bake for 45min at 175°C. Cut a line into the top of the bread after around 5min of baking."""
34 | }
35 |
36 | println(bananaBreadRecipe)
--------------------------------------------------------------------------------
/exercises/src/main/kotlin/chapter09/exercise9_2/Solution.md:
--------------------------------------------------------------------------------
1 | # Exercise 9.2 (Gradle Kotlin DSL)
2 |
3 | The solution for this exercise is in a separate folder in the
4 | [corresponding app repository for the Kudoo app](https://github.com/petersommerhoff/kudoo-app/tree/master/07_GradleKotlinDSL_ExerciseFromInstructorsManual)
5 |
--------------------------------------------------------------------------------
/listings/.idea/compiler.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
--------------------------------------------------------------------------------
/listings/.idea/encodings.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/listings/.idea/kotlinc.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
14 |
15 |
16 |
--------------------------------------------------------------------------------
/listings/.idea/listings.iml:
--------------------------------------------------------------------------------
1 |
2 |
--------------------------------------------------------------------------------
/listings/.idea/markdown-navigator/profiles_settings.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
--------------------------------------------------------------------------------
/listings/.idea/misc.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
--------------------------------------------------------------------------------
/listings/.idea/modules/chapter02_main.iml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
20 |
23 |
24 |
25 |
26 |
27 |
28 |
--------------------------------------------------------------------------------
/listings/.idea/modules/chapter02_test.iml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
20 |
23 |
24 |
25 |
26 |
27 |
28 |
--------------------------------------------------------------------------------
/listings/build.gradle:
--------------------------------------------------------------------------------
1 | buildscript {
2 | ext.kotlin_version = '1.3.31'
3 |
4 | repositories {
5 | mavenCentral()
6 | }
7 | dependencies {
8 | classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
9 | }
10 | }
11 |
12 | group 'com.kotlinandroidbook'
13 | version '1.0-SNAPSHOT'
14 |
15 | apply plugin: 'kotlin'
16 |
17 | repositories {
18 | mavenCentral()
19 | }
20 |
21 | dependencies {
22 | compile "org.jetbrains.kotlin:kotlin-stdlib-jdk8:$kotlin_version"
23 | compile "org.jetbrains.kotlinx:kotlinx-coroutines-core:1.2.1"
24 | compile "org.jetbrains.kotlinx:kotlinx-coroutines-android:1.2.1"
25 | compile "org.jetbrains.kotlinx:kotlinx-coroutines-jdk8:1.2.1"
26 | compile group: 'com.google.guava', name: 'guava', version: '23.5-jre'
27 | compile "org.jetbrains.kotlin:kotlin-script-runtime:$kotlin_version"
28 | }
29 |
30 | compileKotlin {
31 | kotlinOptions.jvmTarget = "1.8"
32 | }
33 | compileTestKotlin {
34 | kotlinOptions.jvmTarget = "1.8"
35 | }
--------------------------------------------------------------------------------
/listings/gradle/wrapper/gradle-wrapper.jar:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/petersommerhoff/kotlin-for-android-app-development/2b3ffe6430ac6389da311f7600a57f766476d157/listings/gradle/wrapper/gradle-wrapper.jar
--------------------------------------------------------------------------------
/listings/gradle/wrapper/gradle-wrapper.properties:
--------------------------------------------------------------------------------
1 | #Sat Aug 18 07:52:13 CEST 2018
2 | distributionBase=GRADLE_USER_HOME
3 | distributionPath=wrapper/dists
4 | zipStoreBase=GRADLE_USER_HOME
5 | zipStorePath=wrapper/dists
6 | distributionUrl=https\://services.gradle.org/distributions/gradle-4.4-all.zip
7 |
--------------------------------------------------------------------------------
/listings/settings.gradle:
--------------------------------------------------------------------------------
1 | rootProject.name = 'listings'
2 |
3 |
--------------------------------------------------------------------------------
/listings/src/main/java/chapter05/Box.java:
--------------------------------------------------------------------------------
1 | package chapter05;
2 |
3 | public class Box {
4 | private final int value;
5 |
6 | public Box(int value) { this.value = value; }
7 | public Box plus(Box other) { return new Box(this.value + other.value); }
8 | public Box minus(Box other) { return new Box(this.value - other.value); }
9 | public int getValue() { return value; }
10 | }
11 |
12 |
--------------------------------------------------------------------------------
/listings/src/main/java/chapter05/GettersAndSetters.java:
--------------------------------------------------------------------------------
1 | package chapter05;
2 |
3 | public class GettersAndSetters {
4 | private String readOnly = "Only getter defined";
5 | private String writeOnly = "Only setter defined";
6 | private String readWrite = "Both defined";
7 |
8 | public String getReadOnly() { return readOnly; }
9 | public void setWriteOnly(String writeOnly) { this.writeOnly = writeOnly; }
10 | public String getReadWrite() { return readWrite; }
11 | public void setReadWrite(String readWrite) { this.readWrite = readWrite; }
12 | }
13 |
14 |
--------------------------------------------------------------------------------
/listings/src/main/java/chapter05/KeywordsAsIdentifiers.java:
--------------------------------------------------------------------------------
1 | package chapter05;
2 |
3 | import java.util.List;
4 |
5 | public class KeywordsAsIdentifiers {
6 | public int val = 100;
7 | public Object object = new Object();
8 | public boolean in(List list) { return true; }
9 | public void fun() { System.out.println("This is fun."); }
10 | }
11 |
12 |
--------------------------------------------------------------------------------
/listings/src/main/java/chapter05/Listing0503.java:
--------------------------------------------------------------------------------
1 | package chapter05;
2 |
3 | public class Listing0503 {
4 | public static String hello() { return "I could be null"; }
5 | }
6 |
--------------------------------------------------------------------------------
/listings/src/main/java/chapter05/Listing0504.java:
--------------------------------------------------------------------------------
1 | package chapter05;
2 |
3 | import org.jetbrains.annotations.NotNull;
4 | import org.jetbrains.annotations.Nullable;
5 |
6 | public class Listing0504 {
7 | public static @Nullable String nullable() { return null; }
8 | public static @NotNull String nonNull() { return "Could still be null, but with a warning"; }
9 | }
10 |
--------------------------------------------------------------------------------
/listings/src/main/java/chapter05/Listing0506.java:
--------------------------------------------------------------------------------
1 | package chapter05;
2 |
3 | import java.util.Arrays;
4 | import java.util.List;
5 |
6 | public class Listing0506 {
7 | public static List myListOf(String... strings) { // Vararg method from Java
8 | return Arrays.asList(strings); // Can pass in vararg unchanged
9 | }
10 | }
11 |
--------------------------------------------------------------------------------
/listings/src/main/java/chapter05/Listing0510.java:
--------------------------------------------------------------------------------
1 | package chapter05;
2 |
3 | public class Listing0510 {
4 | public static void main(String[] args) {
5 | KotlinClass kotlinClass = new KotlinClass();
6 | String s = kotlinClass.getFixed(); // Uses getter of ‘val’
7 | kotlinClass.setMutable(true); // Uses setter of ‘var’
8 | boolean b = kotlinClass.getMutable(); // Uses getter of ‘var’
9 | }
10 | }
11 |
--------------------------------------------------------------------------------
/listings/src/main/java/chapter05/Listing0511.java:
--------------------------------------------------------------------------------
1 | package chapter05;
2 |
3 | public class Listing0511 {
4 | public static void main(String[] args) {
5 | KotlinClass0511 kotlinClass = new KotlinClass0511();
6 | boolean b = kotlinClass.isMutable(); // Now getter is accessible as ‘isMutable’
7 | }
8 | }
9 |
--------------------------------------------------------------------------------
/listings/src/main/java/chapter05/Listing0518.java:
--------------------------------------------------------------------------------
1 | package chapter05;
2 |
3 | public class Listing0518 {
4 | public static void main(String[] args) {
5 | Cache0518.cache("supercar", new Car()); // Static member is callable directly on class
6 | Cache0518.INSTANCE.cache("car", new Car()); // Bad practice
7 |
8 | Car0518.produceCar(); // Static
9 | Car0518.Factory.produceCar(); // Also possible
10 | (new Car0518()).produceCar(); // Bad practice
11 |
12 | }
13 | }
14 |
--------------------------------------------------------------------------------
/listings/src/main/java/chapter05/Listing0519.java:
--------------------------------------------------------------------------------
1 | package chapter05;
2 |
3 | public class Listing0519 {
4 | public static void main(String[] args) {
5 | String[] languages = new String[] {"Kotlin", "Scala", "Java", "Groovy"};
6 |
7 | // Without @JvmOverloads: you must pass in all parameters
8 | ArrayUtils.join(languages, ";", "{", "}"); // Assumes @file:JvmName("ArrayUtils")
9 |
10 | // With @JvmOverloads: overloaded methods
11 | ArrayUtils.join(languages); // Skips all optional parameters
12 | ArrayUtils.join(languages, "; "); // Skips prefix and suffix
13 | ArrayUtils.join(languages, "; ", "Array: "); // Skips suffix
14 | ArrayUtils.join(languages, "; ", "[", "]"); // Passes in all possible arguments
15 | }
16 | }
17 |
--------------------------------------------------------------------------------
/listings/src/main/java/chapter05/Listing0520.java:
--------------------------------------------------------------------------------
1 | package chapter05;
2 |
3 | import static java.lang.System.out;
4 | import static java.util.Arrays.asList;
5 |
6 | public class Listing0520 {
7 | public static void main(String[] args) {
8 | Component comp = new Composite(asList(new Leaf(1), new Leaf(17)));
9 |
10 | if (comp instanceof Composite) { // Cannot use ‘switch’, must use ‘if’
11 | out.println("It's a Composite"); // No smart-casts
12 | } else if (comp instanceof Leaf) { // No exhaustiveness inferred
13 | out.println("It's a Leaf");
14 | }
15 | }
16 | }
17 |
--------------------------------------------------------------------------------
/listings/src/main/java/chapter05/Listing0521.java:
--------------------------------------------------------------------------------
1 | package chapter05;
2 |
3 | import static java.lang.System.out;
4 |
5 | public class Listing0521 {
6 | public static void main(String[] args) {
7 | Person p1 = new Person("Peter", true);
8 | Person p2 = new Person("Marie Curie", false);
9 | Person p3 = p2.copy("Marie Curie", false); // No advantage over constructor
10 |
11 | String name = p1.getName(); // componentN() methods superfluous
12 | out.println(p1); // Calls toString()
13 | out.println(p2.equals(p3)); // true
14 | }
15 | }
16 |
--------------------------------------------------------------------------------
/listings/src/main/java/chapter05/Listing0522.java:
--------------------------------------------------------------------------------
1 | package chapter05;
2 |
3 | import kotlin.jvm.JvmClassMappingKt;
4 | import kotlin.reflect.KClass;
5 |
6 | public class Listing0522 {
7 | public static void main(String[] args) {
8 | KClass clazz = JvmClassMappingKt.getKotlinClass(A.class);
9 | }
10 |
11 | class A {}
12 | }
13 |
--------------------------------------------------------------------------------
/listings/src/main/java/chapter05/Listing0526.java:
--------------------------------------------------------------------------------
1 | package chapter05;
2 |
3 | import java.io.FileNotFoundException;
4 |
5 | public class Listing0526 {
6 | public static void main(String[] args) {
7 | try { // Must handle exception
8 | CsvUtils.readInput(); // Assumes @file:JvmName("CsvUtils")
9 | } catch (FileNotFoundException e) {
10 | // Handle non-existing file...
11 | }
12 | }
13 | }
14 |
--------------------------------------------------------------------------------
/listings/src/main/java/chapter05/Listing0528.java:
--------------------------------------------------------------------------------
1 | package chapter05;
2 |
3 | public class Listing0528 {
4 | public static void main(String[] args) {
5 | Listing0528Kt.consumeStack(new Stack(4, 8, 15, 16, 23, 42));
6 | Listing0528Kt.consumeStack(new Stack(4, 8, 15, 16, 23, 42));
7 | }
8 | }
9 |
--------------------------------------------------------------------------------
/listings/src/main/java/chapter05/Listing0529.java:
--------------------------------------------------------------------------------
1 | package chapter05;
2 |
3 | public class Listing0529 {
4 | public static void main(String[] args) {
5 | Listing0529Kt.consumeStack0529(new Stack(4, 8, 15, 16, 23, 42)); // Matches exactly
6 | Listing0528Kt.consumeStack(new Stack(4, 8, 15, 16, 23, 42)); // Error: No longer allowed
7 |
8 | // Stack stack = Listing0529Kt.produceStack0529(); // Error: No longer allowed
9 | Stack extends Number> stack = Listing0529Kt.produceStack0529(); // Inconvenient, thus bad practice
10 | }
11 | }
12 |
--------------------------------------------------------------------------------
/listings/src/main/java/chapter05/Listing0530.java:
--------------------------------------------------------------------------------
1 | package chapter05;
2 |
3 | public class Listing0530 {
4 | public static void main(String[] args) {
5 | Nothing.takeNothing(null); // Possible in Java (but with warning due to @NonNull)
6 | Nothing.fail("Cannot pass null to non-null variable"); // Cannot terminate
7 | System.out.println("Never reached but Java doesn't know"); // Dead code
8 | }
9 | }
10 |
--------------------------------------------------------------------------------
/listings/src/main/java/chapter05/Producer.java:
--------------------------------------------------------------------------------
1 | package chapter05;
2 |
3 | interface Producer { // SAM interface (single abstract method)
4 | T produce();
5 | }
6 |
7 |
--------------------------------------------------------------------------------
/listings/src/main/kotlin/chapter02/Listing0201.kts:
--------------------------------------------------------------------------------
1 | var mercury: String = "Mercury"
2 | mercury = "Venus" // Mutable so can be reassigned
3 |
4 | print(mercury)
--------------------------------------------------------------------------------
/listings/src/main/kotlin/chapter02/Listing0202.kts:
--------------------------------------------------------------------------------
1 | val mercury: String = "Mercury"
2 | //mercury = "Venus" // Compile-time error: "val cannot be reassigned"
3 |
--------------------------------------------------------------------------------
/listings/src/main/kotlin/chapter02/Listing0203.kts:
--------------------------------------------------------------------------------
1 | val mercury = "Mercury" // Inferred type: String
2 | val maxSurfaceTempInK = 700 // Inferred type: Int
3 | val radiusInKm = 2439.7 // Inferred type: Double
4 |
--------------------------------------------------------------------------------
/listings/src/main/kotlin/chapter02/Listing0204.kts:
--------------------------------------------------------------------------------
1 | val mercury = "Mercury"
2 |
3 | if (mercury == "Mercury") {
4 | println("Universe is intact.")
5 | } else if (mercury == "mercury") {
6 | println("Still all right.")
7 | } else {
8 | println("Universe out of order.")
9 | }
10 |
--------------------------------------------------------------------------------
/listings/src/main/kotlin/chapter02/Listing0205.kts:
--------------------------------------------------------------------------------
1 | val mercury = "Mercury"
2 |
3 | when (mercury) {
4 | "Mercury" -> println("Universe is intact.")
5 | "mercury" -> println("Still all right.")
6 | else -> println("Universe out of order.")
7 | }
8 |
--------------------------------------------------------------------------------
/listings/src/main/kotlin/chapter02/Listing0206.kts:
--------------------------------------------------------------------------------
1 | val maxSurfaceTempInK = 700
2 | fun earthSurfaceTemp(): Int = 273 + 20
3 |
4 | when (maxSurfaceTempInK) {
5 | 700 -> println("This is Mercury’s maximum surface temperature.")
6 | 0, 1, 2 -> println("It’s as cold as it gets.")
7 | in 300..699 -> println("This temperature is also possible on Mercury.")
8 | !in 0..300 -> println("This is pretty hot.")
9 | earthSurfaceTemp() -> println("This is earth’s average surface temperature.")
10 | is Int -> println("Given variable is of type Int.")
11 | else -> {
12 | // more code...
13 | println("Default case")
14 | }
15 | }
16 |
--------------------------------------------------------------------------------
/listings/src/main/kotlin/chapter02/Listing0207.kts:
--------------------------------------------------------------------------------
1 | val age = 19
2 | val hasAccess = true
3 |
4 | when {
5 | age < 18 && hasAccess -> println("False positive")
6 | age > 21 && !hasAccess -> println("False negative")
7 | else -> println("All working as expected")
8 | }
9 |
--------------------------------------------------------------------------------
/listings/src/main/kotlin/chapter02/Listing0208.kts:
--------------------------------------------------------------------------------
1 | val mercury = "Mercury"
2 |
3 | val status = if (mercury == "Mercury") {
4 | "Universe is intact."
5 | } else {
6 | "Universe out of order."
7 | }
8 |
9 | println(status)
--------------------------------------------------------------------------------
/listings/src/main/kotlin/chapter02/Listing0209.kts:
--------------------------------------------------------------------------------
1 | val mercury = "Mercury"
2 |
3 | val status = if (mercury == "Mercury") "Intact" else "Out of order"
4 |
5 | println(status)
--------------------------------------------------------------------------------
/listings/src/main/kotlin/chapter02/Listing0210.kts:
--------------------------------------------------------------------------------
1 | val maxSurfaceTempInK = 700
2 |
3 | val temperatureDescription = when (maxSurfaceTempInK) {
4 | 700 -> "This is Mercury’s maximum surface temperature"
5 | // ...
6 | else -> {
7 | // more code...
8 | "Default case"
9 | }
10 | }
11 |
12 | println(temperatureDescription)
--------------------------------------------------------------------------------
/listings/src/main/kotlin/chapter02/Listing0211.kts:
--------------------------------------------------------------------------------
1 | val number = 42
2 | var approxSqrt = 1.0
3 | var error = 1.0
4 | while (error > 0.0001) {
5 | approxSqrt = 0.5 * (approxSqrt + number / approxSqrt)
6 | error = Math.abs((number - approxSqrt * approxSqrt) / (2 * approxSqrt))
7 | }
8 |
9 | println("The approximated square root is $approxSqrt.")
10 | println("The error is $error.")
--------------------------------------------------------------------------------
/listings/src/main/kotlin/chapter02/Listing0212.kts:
--------------------------------------------------------------------------------
1 | do {
2 | val command = readLine()
3 | // Handle command...
4 | } while (command != ":quit")
5 |
--------------------------------------------------------------------------------
/listings/src/main/kotlin/chapter02/Listing0213.kts:
--------------------------------------------------------------------------------
1 | for (i in 1..100) println(i) // iterating over a range from 1 to 100
2 |
3 | for (i in 1 until 100) println(i) // iterating over a range from 1 to 99
4 |
5 | val planets = listOf("Mercury", "Venus", "Earth", "Mars")
6 |
7 | for (planet in planets) // iterating over a collection
8 | println(planet)
9 |
10 | for (character in "Mercury") { // iterating over a String
11 | println("$character, ")
12 | }
13 |
--------------------------------------------------------------------------------
/listings/src/main/kotlin/chapter02/Listing0214.kts:
--------------------------------------------------------------------------------
1 | for (i in 100 downTo 1) println(i) // 100, 99, 98, ...
2 |
3 | println("======")
4 |
5 | for (i in 1..10 step 2) println(i) // 1, 3, 5, 7, 9
6 |
7 | println("======")
8 |
9 | for (i in 100 downTo 1 step 5) println(i) // 100, 95, 90, ...
10 |
--------------------------------------------------------------------------------
/listings/src/main/kotlin/chapter02/Listing0215.kts:
--------------------------------------------------------------------------------
1 | fun fib(n: Int): Long {
2 | return if (n < 2) {
3 | 1
4 | } else {
5 | fib(n - 1) + fib(n - 2)
6 | }
7 | }
8 |
9 | print(fib(7))
--------------------------------------------------------------------------------
/listings/src/main/kotlin/chapter02/Listing0216.kts:
--------------------------------------------------------------------------------
1 | fun fib(n: Int): Long {
2 | return if (n < 2) {
3 | 1
4 | } else {
5 | fib(n - 1) + fib(n - 2)
6 | }
7 | }
8 |
9 | val fib = fib(7) // 21L
10 |
11 | println(fib)
12 |
--------------------------------------------------------------------------------
/listings/src/main/kotlin/chapter02/Listing0217.kts:
--------------------------------------------------------------------------------
1 | fun fib(n: Int): Long {
2 | return if (n < 2) 1 else fib(n - 1) + fib(n - 2)
3 | }
4 |
--------------------------------------------------------------------------------
/listings/src/main/kotlin/chapter02/Listing0218.kts:
--------------------------------------------------------------------------------
1 | fun fib(n: Int): Long = if (n < 2) 1 else fib(n-1) + fib(n-2)
2 |
--------------------------------------------------------------------------------
/listings/src/main/kotlin/chapter02/Listing0219.kt:
--------------------------------------------------------------------------------
1 | fun main() {
2 | println("Hello World!")
3 | }
4 |
--------------------------------------------------------------------------------
/listings/src/main/kotlin/chapter02/Listing0220.kts:
--------------------------------------------------------------------------------
1 | fun exploreDirectory(path: String, depth: Int = 0) {
2 | // ...
3 | }
4 |
--------------------------------------------------------------------------------
/listings/src/main/kotlin/chapter02/Listing0221.kts:
--------------------------------------------------------------------------------
1 | fun exploreDirectory(path: String, depth: Int = 0) {}
2 |
3 | val directory = "/home/johndoe/pictures"
4 |
5 | exploreDirectory(directory) // Without optional argument, depth is 0
6 | exploreDirectory(directory, 1) // Recursion depth set to 1
7 | exploreDirectory(directory, depth = 2) // Uses named parameter to set depth
8 | exploreDirectory(depth = 3, path = directory) // Uses named parameters to change order
9 |
--------------------------------------------------------------------------------
/listings/src/main/kotlin/chapter02/Listing0222.kts:
--------------------------------------------------------------------------------
1 | class HTML
2 |
3 | fun sendEmail(message: String) {}
4 | fun sendEmail(message: HTML) {}
5 |
--------------------------------------------------------------------------------
/listings/src/main/kotlin/chapter02/Listing0223.kts:
--------------------------------------------------------------------------------
1 | import java.util.*
2 |
3 | fun Date.plusDays(n: Int) = Date(this.time + n * 86400000)
4 | val now = Date()
5 | val tomorrow = now.plusDays(1)
6 |
7 | println(now)
8 | println(tomorrow)
--------------------------------------------------------------------------------
/listings/src/main/kotlin/chapter02/Listing0224.kts:
--------------------------------------------------------------------------------
1 | /**
2 | * @author Peter Sommerhoff
3 | */
4 |
5 | fun Number.print() = println("Number $this") // Extension on supertype
6 | fun Int.print() = println("Int $this") // Extension on subtype
7 |
8 | val n: Number = 42 // Statically resolved type: Number
9 | n.print() // Prints "Number 42"
10 |
--------------------------------------------------------------------------------
/listings/src/main/kotlin/chapter02/Listing0225.kt:
--------------------------------------------------------------------------------
1 | // import com.example.time.plusDays
--------------------------------------------------------------------------------
/listings/src/main/kotlin/chapter02/Listing0226.kts:
--------------------------------------------------------------------------------
1 | infix fun A.to(that: B) = Pair(this, that) // Declared in standard library
--------------------------------------------------------------------------------
/listings/src/main/kotlin/chapter02/Listing0227.kts:
--------------------------------------------------------------------------------
1 | val pair = "Kotlin" to "Android" // Pair("Kotlin","Android")
2 | val userToScore = mapOf("Peter" to 0.82, "John" to 0.97) // Creates a map
3 |
--------------------------------------------------------------------------------
/listings/src/main/kotlin/chapter02/Listing0228.kts:
--------------------------------------------------------------------------------
1 | infix fun Int.times(str: String) = str.repeat(this)
2 |
3 | val message = 3 times "Kotlin " // Results in "Kotlin Kotlin Kotlin "
4 |
--------------------------------------------------------------------------------
/listings/src/main/kotlin/chapter02/Listing0229.kts:
--------------------------------------------------------------------------------
1 | operator fun Int.times(str: String) = str.repeat(this)
2 |
3 | val message = 3 * "Kotlin " // Still results in "Kotlin Kotlin Kotlin "
4 |
--------------------------------------------------------------------------------
/listings/src/main/kotlin/chapter02/Listing0230.kts:
--------------------------------------------------------------------------------
1 | val name: String? = "John Doe" // Nullable variable of type ‘String?’
2 | val title: String? = null // Nullable variables may hold ‘null’
3 |
4 | //val wrong: String = null // Causes compiler error because not nullable
5 |
--------------------------------------------------------------------------------
/listings/src/main/kotlin/chapter02/Listing0231.kts:
--------------------------------------------------------------------------------
1 | val name: String? = "John Doe"
2 | //println(name.length) // Not safe => causes compile-time error
3 |
4 | if (name != null) { // Explicit null check
5 | println(name.length) // Now safe so compiler allows accessing length
6 | }
7 |
8 | println(name?.length) // Safe call operator ‘?’
9 | val upper = name?.toUpperCase() // Safe call operator
10 |
--------------------------------------------------------------------------------
/listings/src/main/kotlin/chapter02/Listing0232.kts:
--------------------------------------------------------------------------------
1 | val name: String? = null
2 | val len = name?.length ?: 0 // Assigns length, or else zero as the default value
3 |
--------------------------------------------------------------------------------
/listings/src/main/kotlin/chapter02/Listing0233.kts:
--------------------------------------------------------------------------------
1 | val name: String? = "John Doe" // May also be null
2 | val len = name!!.length // Asserts the compiler that name is not null; unsafe access
3 |
--------------------------------------------------------------------------------
/listings/src/main/kotlin/chapter02/Listing0234.kts:
--------------------------------------------------------------------------------
1 | val name: String? = "Peter"
2 |
3 | fun greet(name: String) = println("Hi $name!")
4 |
5 | greet(name!!) // Better be sure that ‘name’ cannot be null here
6 | greet(name ?: "Anonymous") // Safe alternative using elvis operator
7 |
--------------------------------------------------------------------------------
/listings/src/main/kotlin/chapter02/Listing0235.kts:
--------------------------------------------------------------------------------
1 | val list1 = listOf(1, 2, 3) // List object
2 | val list2 = listOf(1, 2, 3) // Different list object but with same contents
3 |
4 | println(list1 === list2) // false: the variables reference different objects in memory
5 | println(list1 == list2) // true: the objects they reference are equal
6 | println(list1 !== list2) // true: negation of first comparison
7 | println(list1 != list2) // false: negation of second comparison
8 |
--------------------------------------------------------------------------------
/listings/src/main/kotlin/chapter02/Listing0236.kts:
--------------------------------------------------------------------------------
1 | val negativeZero = -0.0f // Statically inferred type: Float
2 | val positiveZero = 0.0f // Statically inferred type: Float
3 | println(Float.NaN == Float.NaN) // false (IEEE standard)
4 | println(negativeZero == positiveZero) // true (IEEE standard)
5 |
6 | val nan: Any = Float.NaN // Explicit type: Any => not Float or Double
7 | val negativeZeroAny: Any = -0.0f // Explicit type: Any
8 | val positiveZeroAny: Any = 0.0f // Explicit type: Any
9 |
10 | println(nan == nan) // true (not IEEE standard)
11 | println(negativeZeroAny == positiveZeroAny) // false (not IEEE standard)
12 |
--------------------------------------------------------------------------------
/listings/src/main/kotlin/chapter02/Listing0237.kts:
--------------------------------------------------------------------------------
1 | fun reducePressureBy(bar: Int) {
2 | // …
3 | throw IllegalArgumentException("Invalid pressure reduction by $bar")
4 | }
5 |
6 | try {
7 | reducePressureBy(30)
8 | } catch (e: IllegalArgumentException) {
9 | // Handle exception here
10 | } catch (e: IllegalStateException) {
11 | // You can handle multiple possible exceptions
12 | } finally {
13 | // This code will always execute at the end of the block
14 | }
15 |
--------------------------------------------------------------------------------
/listings/src/main/kotlin/chapter02/Listing0238.kts:
--------------------------------------------------------------------------------
1 | val inputString = "4815162342"
2 |
3 | val input: Int? = try {
4 | inputString.toInt() // Tries to parse input to an integer
5 | } catch (e: NumberFormatException) {
6 | null // Returns null if input could not be parsed to integer
7 | }
8 |
--------------------------------------------------------------------------------
/listings/src/main/kotlin/chapter02/Listing0239.kts:
--------------------------------------------------------------------------------
1 | class Bitmap { fun prepareToDraw() {} }
2 | class Card(val bitmap: Bitmap?)
3 |
4 | val card = Card(null)
5 | // -----------
6 |
7 | val bitmap = card.bitmap ?: throw IllegalArgumentException("Bitmap required")
8 | bitmap.prepareToDraw() // Known to have type Bitmap here (non-nullable)
9 |
--------------------------------------------------------------------------------
/listings/src/main/kotlin/chapter02/Listing0240.kts:
--------------------------------------------------------------------------------
1 | class Bitmap { fun prepareToDraw() {} }
2 | class Card(val bitmap: Bitmap?)
3 |
4 | val card = Card(Bitmap())
5 | // -----------
6 |
7 | fun fail(message: String): Nothing { // Nothing type => function never terminates
8 | throw IllegalArgumentException(message)
9 | }
10 |
11 | val bitmap = card.bitmap ?: fail("Bitmap required") // Inferred type: Bitmap
12 | bitmap.prepareToDraw()
13 |
--------------------------------------------------------------------------------
/listings/src/main/kotlin/chapter03/Listing0301.kts:
--------------------------------------------------------------------------------
1 | fun countOccurrences(of: Char, inStr: String): Int { // This part is the function _signature_
2 | return inStr.count { it == of }
3 | }
--------------------------------------------------------------------------------
/listings/src/main/kotlin/chapter03/Listing0302.kts:
--------------------------------------------------------------------------------
1 | fun countOccurrences(of: Char, inStr: String): Int {
2 | return inStr.count { it == of }
3 | }
4 |
5 | val count: (Char, String) -> Int = ::countOccurrences // Function reference matches function type
--------------------------------------------------------------------------------
/listings/src/main/kotlin/chapter03/Listing0303.kts:
--------------------------------------------------------------------------------
1 | { x: Int, y: Int -> x + y }
--------------------------------------------------------------------------------
/listings/src/main/kotlin/chapter03/Listing0304.kts:
--------------------------------------------------------------------------------
1 | val sum: (Int, Int) -> Int = { x: Int, y: Int -> x + y }
--------------------------------------------------------------------------------
/listings/src/main/kotlin/chapter03/Listing0305.kts:
--------------------------------------------------------------------------------
1 | val sum: (Int, Int) -> Int = { x, y -> x + y } // Infers the lambda parameter types
--------------------------------------------------------------------------------
/listings/src/main/kotlin/chapter03/Listing0306.kts:
--------------------------------------------------------------------------------
1 | val sum = { x: Int, y: Int -> x + y } // Infers the variable type
--------------------------------------------------------------------------------
/listings/src/main/kotlin/chapter03/Listing0307.kts:
--------------------------------------------------------------------------------
1 | val toUpper: (String) -> String = { it.toUpperCase() } // Uses the implicit ‘it’
--------------------------------------------------------------------------------
/listings/src/main/kotlin/chapter03/Listing0308.kts:
--------------------------------------------------------------------------------
1 | val toUpper: (String) -> String = String::toUpperCase // Uses function reference
--------------------------------------------------------------------------------
/listings/src/main/kotlin/chapter03/Listing0309.kts:
--------------------------------------------------------------------------------
1 | fun twice(f: (Int) -> Int): (Int) -> Int = { x -> f(f(x)) } // Applies ‘f’ twice
--------------------------------------------------------------------------------
/listings/src/main/kotlin/chapter03/Listing0310.kts:
--------------------------------------------------------------------------------
1 | fun twice(f: (Int) -> Int): (Int) -> Int = { x -> f(f(x)) } // Applies ‘f’ twice
2 | // ---------
3 |
4 | val plusTwo = twice({ it + 1 }) // Uses lambda
5 | val plus2 = twice(Int::inc) // Uses function reference
6 |
--------------------------------------------------------------------------------
/listings/src/main/kotlin/chapter03/Listing0311.kts:
--------------------------------------------------------------------------------
1 | fun twice(f: (Int) -> Int): (Int) -> Int = { x -> f(f(x)) } // Applies ‘f’ twice
2 | // ---------
3 |
4 | val plusTwo = twice { it + 1 } // Moves lambda out of parentheses and omits parens
--------------------------------------------------------------------------------
/listings/src/main/kotlin/chapter03/Listing0312.kts:
--------------------------------------------------------------------------------
1 | fun twice(f: (Int) -> Int): (Int) -> Int = { x -> f(f(x)) } // Applies ‘f’ twice
2 | // ---------
3 |
4 | val plusTwo = twice { it + 1 } // Creates a function object
--------------------------------------------------------------------------------
/listings/src/main/kotlin/chapter03/Listing0313.kts:
--------------------------------------------------------------------------------
1 | (1..10).forEach { // This function call is inlined at compile-time
2 | println(it)
3 | }
--------------------------------------------------------------------------------
/listings/src/main/kotlin/chapter03/Listing0314.kts:
--------------------------------------------------------------------------------
1 | for (element in 1..10) // No lambda, thus no object at runtime
2 | println(element)
--------------------------------------------------------------------------------
/listings/src/main/kotlin/chapter03/Listing0315.kts:
--------------------------------------------------------------------------------
1 | fun contains(range: IntRange, number: Int): Boolean {
2 | range.forEach {
3 | if (it == number) return true // Can use return because lambda is inlined
4 | }
5 | return false
6 | }
7 |
--------------------------------------------------------------------------------
/listings/src/main/kotlin/chapter03/Listing0316.kts:
--------------------------------------------------------------------------------
1 | fun contains(range: IntRange, number: Int): Boolean {
2 | for (element in range) {
3 | if (element == number) return true // This return is equivalent to the one above
4 | }
5 | return false
6 | }
7 |
--------------------------------------------------------------------------------
/listings/src/main/kotlin/chapter03/Listing0317.kts:
--------------------------------------------------------------------------------
1 | inline fun twice(crossinline f: (Int) -> Int): (Int) -> Int = { x -> f(f(x)) }
--------------------------------------------------------------------------------
/listings/src/main/kotlin/chapter03/Listing0318.kts:
--------------------------------------------------------------------------------
1 | // Collections
2 | val languages = setOf("Kotlin", "Java", "C++") // Creates a read-only set
3 | val votes = listOf(true, false, false, true) // Creates a read-only list
4 | val countryToCapital = mapOf( // Creates a read-only map
5 | "Germany" to "Berlin", "France" to "Paris")
6 |
7 | // Arrays
8 | val testData = arrayOf(0, 1, 5, 9, 10) // Creates an array (mutable)
9 |
--------------------------------------------------------------------------------
/listings/src/main/kotlin/chapter03/Listing0319.kts:
--------------------------------------------------------------------------------
1 | val votes = listOf(true, false, false, true) // Creates a read-only list
2 | val countryToCapital = mapOf( // Creates a read-only map
3 | "Germany" to "Berlin", "France" to "Paris")
4 | val testData = arrayOf(0, 1, 5, 9, 10) // Creates an array (mutable)
5 | // ------------
6 |
7 | votes[1] // false
8 | testData[2] // 5
9 | countryToCapital["Germany"] // "Berlin"
10 |
--------------------------------------------------------------------------------
/listings/src/main/kotlin/chapter03/Listing0320.kts:
--------------------------------------------------------------------------------
1 | val votes = mutableListOf(true, false, false, true) // Creates a read-only list
2 | val countryToCapital = mutableMapOf( // Creates a read-only map
3 | "Germany" to "Berlin", "France" to "Paris")
4 | val testData = arrayOf(0, 1, 5, 9, 10) // Creates an array (mutable)
5 | // ------------
6 |
7 | // Assumes mutable collections now
8 | votes[1] = true // Replaces second element with true
9 | testData[2] = 4 // Replaces third element with 4
10 | countryToCapital["Germany"] = "Bonn" // Replaces value for key "Germany"
11 |
--------------------------------------------------------------------------------
/listings/src/main/kotlin/chapter03/Listing0321.kts:
--------------------------------------------------------------------------------
1 | val votes = listOf(true, false, false, true) // Creates a read-only list
2 | // ------------
3 |
4 | val numberOfYesVotes = votes.filter { it == true }.count() // Counts positive votes
--------------------------------------------------------------------------------
/listings/src/main/kotlin/chapter03/Listing0322.kts:
--------------------------------------------------------------------------------
1 | val countryToCapital = mapOf( // Creates a read-only map
2 | "Germany" to "Berlin", "France" to "Paris")
3 | // ------------
4 |
5 | val searchResult = countryToCapital.keys.filter { it.toUpperCase().startsWith('F') }
--------------------------------------------------------------------------------
/listings/src/main/kotlin/chapter03/Listing0323.kts:
--------------------------------------------------------------------------------
1 | val testData = arrayOf(0, 1, 5, 9, 10) // Creates an array (mutable)
2 | // ------------
3 |
4 | val squared = testData.map { it * it } // Creates list with each element squared
--------------------------------------------------------------------------------
/listings/src/main/kotlin/chapter03/Listing0324.kts:
--------------------------------------------------------------------------------
1 | inline fun Iterable.map(transform: (T) -> R): List { // Returns List
2 | return listOf() // Just to make it compile
3 | }
--------------------------------------------------------------------------------
/listings/src/main/kotlin/chapter03/Listing0325.kts:
--------------------------------------------------------------------------------
1 | val countryToCapital = mapOf( // Creates a read-only map
2 | "Germany" to "Berlin", "France" to "Paris")
3 | // ------------
4 |
5 | val countries = countryToCapital.map { it.key } // ["Germany", "France"]
6 | println(countries)
--------------------------------------------------------------------------------
/listings/src/main/kotlin/chapter03/Listing0326.kts:
--------------------------------------------------------------------------------
1 | val string = "This is an example sentence with several words in it"
2 | val lengthToWords = string.split(" ") // ["This", "is", "an", …, "in", "it"]
3 | .groupBy { it.length }
4 |
5 | println(lengthToWords) // {4=[This, with], 2=[is, an, in, it], … }
6 |
--------------------------------------------------------------------------------
/listings/src/main/kotlin/chapter03/Listing0327.kts:
--------------------------------------------------------------------------------
1 | val words = listOf("filter", "map", "sorted", "groupBy", "associate")
2 | var id = 0 // The first map key
3 | val map = words.associate { id++ to it } // Associates incrementing keys to ele-ments
4 | println(map) // {0=filter, 1=map, 2=sorted, 3=groupBy, 4=associate}
5 |
--------------------------------------------------------------------------------
/listings/src/main/kotlin/chapter03/Listing0328.kts:
--------------------------------------------------------------------------------
1 | data class User(val age: Int, val score: Int, val monthlyFee: Int)
2 | val users = listOf(User(11, 9000, 0), User(22, 3439, 19), User(42, 984, 99))
3 | // -------------
4 |
5 | println(users.minBy { it.age }) // Returns user object of youngest user
6 | println(users.maxBy { it.score }) // Returns user object of user with maximum score
7 | println(users.sumBy { it.monthlyFee }) // Returns sum of all users’ monthly fees
8 |
--------------------------------------------------------------------------------
/listings/src/main/kotlin/chapter03/Listing0329.kts:
--------------------------------------------------------------------------------
1 | val languages = setOf("Kotlin", "Java", "C++") // Creates a read-only set
2 | val votes = listOf(true, false, false, true) // Creates a read-only list
3 | val countryToCapital = mapOf( // Creates a read-only map
4 | "Germany" to "Berlin", "France" to "Paris")
5 | val testData = arrayOf(0, 1, 5, 9, 10) // Creates an array (mutable)
6 | // ------------
7 |
8 | println(languages.sorted()) // ["C++", "Java", "Kotlin"]
9 | println(languages.sortedDescending()) // ["Kotlin", "Java", "C++"]
10 | println(testData.sortedBy { it % 3 }) // [0, 9, 1, 10, 5]
11 | println(votes.sortedWith(
12 | Comparator { o1, _ -> if (o1 == true) -1 else 1 })) // [true, true, false, false]
13 | println(countryToCapital.toSortedMap()) // {France=Paris, Germany=Berlin}
14 |
--------------------------------------------------------------------------------
/listings/src/main/kotlin/chapter03/Listing0330.kts:
--------------------------------------------------------------------------------
1 | val testData = arrayOf(0, 1, 5, 9, 10)
2 | val sum = testData.fold(0) { acc, element -> acc + element } // 25
3 | val product = testData.fold(1) { acc, element -> acc * element } // 0
4 |
5 | println(sum)
6 | println(product)
--------------------------------------------------------------------------------
/listings/src/main/kotlin/chapter03/Listing0331.kts:
--------------------------------------------------------------------------------
1 | data class User(val username: String, val isActive: Boolean)
2 | val users = listOf(User("admin", true), User("guest", false), User("foo", true))
3 | // ----------
4 |
5 | val activeUserNames = users.filter { it.isActive } // Filter active users
6 | .take(10) // Take first ten users
7 | .map { it.username } // Extract their usernames
8 |
9 | println(activeUserNames)
--------------------------------------------------------------------------------
/listings/src/main/kotlin/chapter03/Listing0332.kts:
--------------------------------------------------------------------------------
1 | import java.io.File
2 | // -----------
3 |
4 | val lines = File("rawdata.csv").bufferedReader().let { // Scopes the buffered reader
5 | val result = it.readLines() // Accesses reader as ‘it’
6 | it.close()
7 | result
8 | }
9 |
--------------------------------------------------------------------------------
/listings/src/main/kotlin/chapter03/Listing0333.kts:
--------------------------------------------------------------------------------
1 | data class Weather(val temperature: Int)
2 | fun fetchWeatherOrNull() = Weather(23)
3 | fun updateUi(degrees: Int) { println("$degrees°C") }
4 | // ----------
5 |
6 | val weather: Weather? = fetchWeatherOrNull()
7 |
8 | weather?.let {
9 | updateUi(weather.temperature) // Only updates UI if ‘weather’ is not null
10 | }
11 |
--------------------------------------------------------------------------------
/listings/src/main/kotlin/chapter03/Listing0334.kts:
--------------------------------------------------------------------------------
1 | val countryToCapital = mutableMapOf( // Creates a read-only map
2 | "Germany" to "Berlin", "France" to "Paris")
3 | // ------------
4 |
5 | countryToCapital.apply {
6 | putIfAbsent("India", "Delhi") // Accesses ‘countryToCapital’ methods without prefix
7 | putIfAbsent("France", "Paris")
8 | }
9 |
10 | println(countryToCapital) // {"Germany"="Berlin", "France"="Paris", "India"="Delhi"}
11 |
--------------------------------------------------------------------------------
/listings/src/main/kotlin/chapter03/Listing0335.kts:
--------------------------------------------------------------------------------
1 | import java.awt.Container
2 | import java.awt.Dimension
3 | import java.awt.Font
4 |
5 | val container = Container().apply { // Initializes object inside ‘apply’
6 | size = Dimension(1024, 800)
7 | font = Font.decode("Arial-bold-22")
8 | isVisible = true
9 | }
10 |
11 | println(container)
--------------------------------------------------------------------------------
/listings/src/main/kotlin/chapter03/Listing0336.kts:
--------------------------------------------------------------------------------
1 | val countryToCapital = mutableMapOf( // Creates a read-only map
2 | "Germany" to "Berlin", "France" to "Paris")
3 | // ------------
4 |
5 | countryToCapital.apply {
6 | putIfAbsent("China", "Beijing")
7 | }.filter {
8 | it.key.startsWith("C")
9 | }.map {
10 | it.value.toUpperCase()
11 | }
--------------------------------------------------------------------------------
/listings/src/main/kotlin/chapter03/Listing0337.kts:
--------------------------------------------------------------------------------
1 | val countryToCapital = mutableMapOf("Germany" to "Berlin")
2 |
3 | val countries = with(countryToCapital) {
4 | putIfAbsent("England", "London")
5 | putIfAbsent("Spain", "Madrid")
6 | keys // Defines return value of the lambda, and therefore of ‘with’
7 | }
8 |
9 | println(countryToCapital) // {"Germany"="Berlin","England"="London","Spain"="Madrid"}
10 | println(countries) // ["Germany", "England", "Spain"]
11 |
--------------------------------------------------------------------------------
/listings/src/main/kotlin/chapter03/Listing0338.kts:
--------------------------------------------------------------------------------
1 | val essay = with(StringBuilder()) { // String builder only accessible inside lambda
2 | appendln("Intro")
3 | appendln("Content")
4 | appendln("Conclusion")
5 | toString()
6 | }
7 |
8 | println(essay)
--------------------------------------------------------------------------------
/listings/src/main/kotlin/chapter03/Listing0339.kts:
--------------------------------------------------------------------------------
1 | val countryToCapital: MutableMap? = mutableMapOf("Germany" to "Berlin")
2 |
3 | val countries = countryToCapital?.run { // Runs block only if not null
4 | putIfAbsent("Mexico", "Mexico City")
5 | putIfAbsent("Germany", "Berlin")
6 | keys
7 | }
8 |
9 | println(countryToCapital) // {Germany=Berlin, Mexico=Mexico City}
10 | println(countries) // ["Germany", "Mexico"]
11 |
--------------------------------------------------------------------------------
/listings/src/main/kotlin/chapter03/Listing0340.kts:
--------------------------------------------------------------------------------
1 | run {
2 | println("Running lambda")
3 | val a = 11 * 13
4 | }
5 |
--------------------------------------------------------------------------------
/listings/src/main/kotlin/chapter03/Listing0341.kts:
--------------------------------------------------------------------------------
1 | fun getUsername() = "guest"
2 | fun getPassword() = ""
3 | fun validate(name: String, pw: String) = (name == "guest" && pw == "")
4 | // ---------
5 |
6 | val success = run {
7 | val username = getUsername() // Only visible inside this lambda
8 | val password = getPassword() // Only visible inside this lambda
9 |
10 | validate(username, password)
11 | }
12 |
13 | println(success)
--------------------------------------------------------------------------------
/listings/src/main/kotlin/chapter03/Listing0342.kts:
--------------------------------------------------------------------------------
1 | data class User(val username: String, val paid: Boolean)
2 | // ----------
3 |
4 | fun renderUsername(user: User) = user.run {
5 | val premium = if (paid) " (Premium)" else "" // Accesses user.paid
6 | val displayName = "$username$premium" // Accesses user.username
7 | println(displayName)
8 | }
9 |
--------------------------------------------------------------------------------
/listings/src/main/kotlin/chapter03/Listing0343.kts:
--------------------------------------------------------------------------------
1 | data class User(val age: Int, val score: Int, val monthlyFee: Int)
2 | fun fetchUser(): User? = User(21, 1337, 29)
3 |
4 | val user = fetchUser().also {
5 | requireNotNull(it)
6 | require(it!!.monthlyFee > 0)
7 | }
--------------------------------------------------------------------------------
/listings/src/main/kotlin/chapter03/Listing0344.kts:
--------------------------------------------------------------------------------
1 | data class User(val age: Int, val score: Int, val monthlyFee: Int)
2 | val users = listOf(User(11, 9000, 0), User(22, 3439, 19), User(42, 984, 99))
3 | // -------------
4 |
5 | users.filter { it.age > 21 }
6 | .also { println("${it.size} adult users found.") } // Intercepts chain
7 | .map { it.monthlyFee }
8 |
9 | println(users)
--------------------------------------------------------------------------------
/listings/src/main/kotlin/chapter03/Listing0345.kts:
--------------------------------------------------------------------------------
1 | import java.io.File
2 | // ----------
3 |
4 | val lines = File("rawdata.csv").bufferedReader().use { it.readLines() }
--------------------------------------------------------------------------------
/listings/src/main/kotlin/chapter03/Listing0346.kts:
--------------------------------------------------------------------------------
1 | class SqlQuery() {
2 | fun append(query: String) {}
3 | fun bind(value: Any) {}
4 | }
5 | class DbConnection() {
6 | fun execute(query: SqlQuery) {}
7 | }
8 | // ------------
9 |
10 | val sql = SqlQuery().apply { // ‘apply’ initializes object
11 | append("INSERT INTO user (username, age, paid) VALUES (?, ?, ?)")
12 | bind("petersommerhoff")
13 | bind(26)
14 | bind(true)
15 | }.also {
16 | println("Initialized SQL query: $it") // ‘also’ intercepts computation chain
17 | }.run {
18 | DbConnection().execute(this) // ‘run’ applies given operations
19 | }
20 |
--------------------------------------------------------------------------------
/listings/src/main/kotlin/chapter03/Listing0347.kts:
--------------------------------------------------------------------------------
1 | val authorsToBooks = mutableMapOf("Joshua Bloch" to listOf("Effective Java"))
2 | // -------------
3 |
4 | val authors = authorsToBooks.apply {
5 | putIfAbsent("Martin Fowler", listOf("Patterns of Enterprise Application Architecture"))
6 | }.filter {
7 | it.value.isNotEmpty()
8 | }.also {
9 | println("Authors with books: ${it.keys}")
10 | }.map {
11 | it.key
12 | }
13 |
--------------------------------------------------------------------------------
/listings/src/main/kotlin/chapter03/Listing0348.kts:
--------------------------------------------------------------------------------
1 | fun T.let(block: (T) -> R): R = block(this) // Lambda with parameter (T) -> R
2 | fun T.run(block: T.() -> R): R = block() // Lambda with receiver T.() -> R
--------------------------------------------------------------------------------
/listings/src/main/kotlin/chapter03/Listing0349.kts:
--------------------------------------------------------------------------------
1 | // Can use eager collection and lazy sequence in same way
2 | val animals = listOf("Dog", "Cat", "Chicken", "Frog") // Eager
3 | //val animals = sequenceOf("Dog", "Cat", "Chicken", "Frog") // Lazy
4 |
5 | animals.filter { it.startsWith("C") }
6 | .map { "$it starts with a ‘C’" }
7 | .take(1)
8 |
--------------------------------------------------------------------------------
/listings/src/main/kotlin/chapter03/Listing0350.kts:
--------------------------------------------------------------------------------
1 | val sequence = sequenceOf(-5, 0, 5)
2 | println(sequence.joinToString()) // -5, 0, 5
3 |
--------------------------------------------------------------------------------
/listings/src/main/kotlin/chapter03/Listing0351.kts:
--------------------------------------------------------------------------------
1 | val cities = listOf("Warsaw", "London", "Washington", "Paris", "Beijing", "Worcester")
2 | // ---------------
3 |
4 | val output = cities.asSequence() // Transform eager list into a lazy sequence
5 | .filter { it.startsWith("W") }
6 | .map { "City: $it" }
7 | .joinToString()
8 |
9 | println(output) // City: Warsaw, City: Washington, City: …
10 |
--------------------------------------------------------------------------------
/listings/src/main/kotlin/chapter03/Listing0352.kts:
--------------------------------------------------------------------------------
1 | val naturalNumbers = generateSequence(0) { it + 1 } // Next element = previous + 1
2 | val integers = generateSequence(0) { if (it > 0) -it else -it + 1 }
3 |
4 | println(naturalNumbers.take(10).joinToString())
5 | println(integers.take(10).joinToString())
--------------------------------------------------------------------------------
/listings/src/main/kotlin/chapter03/Listing0353.kts:
--------------------------------------------------------------------------------
1 | val cities = listOf("Washington", "Houston", "Seattle", "Worcester", "San Francisco")
2 | val firstTwo = cities.take(2) // Washington, Houston
3 | val rest = cities.drop(2) // Seattle, Worcester, San Francisco
4 | firstTwo + rest == cities // true
5 |
6 | println(firstTwo)
7 | println(rest)
8 | println(firstTwo + rest == cities)
--------------------------------------------------------------------------------
/listings/src/main/kotlin/chapter03/Listing0354.kts:
--------------------------------------------------------------------------------
1 | val cities = listOf("Warsaw", "London", "Washington", "Paris", "Beijing")
2 | // ---------------
3 |
4 | // Not good
5 | println(cities.filter { it.startsWith("W") }
6 | .map { "City: $it" } // Calls map before take (could be a million map calls)
7 | .take(20) // Takes only the first 20 results
8 | .joinToString())
9 |
10 | // Better
11 | println(cities.filter { it.startsWith("W") }
12 | .take(20) // Reduces the size of the collection earlier
13 | .map { "City: $it" } // Calls map at most 20 times
14 | .joinToString())
15 |
--------------------------------------------------------------------------------
/listings/src/main/kotlin/chapter03/Listing0355.kts:
--------------------------------------------------------------------------------
1 | val cities = listOf("Washington", "Houston", "Seattle", "Worcester", "San Francisco")
2 |
3 | val output = cities.filter { println("filter: $it"); it.startsWith("W") } // Washington,Worcester
4 | .map { println("map: $it"); "City: $it" } // City: Washington, City: Worcester
5 | .take(2) // Should better be called before ‘map’
6 |
7 | println(output) // Produces Listing 3.56
--------------------------------------------------------------------------------
/listings/src/main/kotlin/chapter03/Listing0357.kts:
--------------------------------------------------------------------------------
1 | val cities = listOf("Washington", "Houston", "Seattle", "Worcester", "San Francisco")
2 |
3 | val output = cities.asSequence()
4 | .filter { println("filter: $it"); it.startsWith("W") }
5 | .map { println("map: $it"); "City: $it" }
6 | .take(2) // Should still better be called before map
7 | .toList()
8 |
9 | println(output) // Produces Listing 3.58
--------------------------------------------------------------------------------
/listings/src/main/kotlin/chapter04/Listing0401.kts:
--------------------------------------------------------------------------------
1 | class Task {
2 | // Implement class here...
3 | }
4 |
5 | val laundry = Task()
6 |
--------------------------------------------------------------------------------
/listings/src/main/kotlin/chapter04/Listing0402.kts:
--------------------------------------------------------------------------------
1 | class Task {
2 | val title: String // Declares a property
3 |
4 | constructor(title: String) {
5 | this.title = title // Initializes the property
6 | }
7 | }
8 |
9 | val dishes = Task("Wash dishes") // Calls constructor to create Task object
10 | val laundry = Task("Do laundry") // Calls constructor with a different title
11 |
--------------------------------------------------------------------------------
/listings/src/main/kotlin/chapter04/Listing0403.kts:
--------------------------------------------------------------------------------
1 | class Task(title: String) { // Primary constructor with one parameter
2 | val title: String = title // Initializes property using constructor argument
3 | }
4 |
5 | val laundry = Task("Do laundry") // Calls constructor as before
6 |
--------------------------------------------------------------------------------
/listings/src/main/kotlin/chapter04/Listing0404.kts:
--------------------------------------------------------------------------------
1 | class Task(val title: String) // Primary constructor declares property directly
2 |
3 | val laundry = Task("Do laundry")
4 |
--------------------------------------------------------------------------------
/listings/src/main/kotlin/chapter04/Listing0405.kts:
--------------------------------------------------------------------------------
1 | class Task(var title: String) // Uses ‘var’ now to be able to call the setter
2 |
3 | val laundry = Task("Do laundry")
4 |
5 | laundry.title = "Laundry day" // Calls setter
6 | println(laundry.title) // Calls getter
7 |
--------------------------------------------------------------------------------
/listings/src/main/kotlin/chapter04/Listing0406.kts:
--------------------------------------------------------------------------------
1 | class Task(title: String, priority: Int) {
2 | val title: String = title
3 | get() = field.toUpperCase() // Defines custom getter
4 |
5 | var priority: Int = priority
6 | get() = field // Same as default implementation, no need to define
7 | set(value) { // Defines custom setter
8 | if (value in 0..100) field = value else throw IllegalArgumentException("…")
9 | }
10 | }
11 |
12 | val laundry = Task("Do laundry", 40)
13 | println(laundry.title) // Calls getter, returns "DO LAUNDRY"
14 | laundry.priority = 150 // Calls setter, throws IllegalArgumentException
15 | println(laundry.priority) // Calls getter, returns 40
16 |
--------------------------------------------------------------------------------
/listings/src/main/kotlin/chapter04/Listing0407.kts:
--------------------------------------------------------------------------------
1 | annotation class BeforeEach
2 | class Car
3 | class TestFactory {
4 | companion object { fun car(): Car = Car() }
5 | }
6 | // -----------------
7 |
8 | class CarTest {
9 | lateinit var car: Car // No initialization required here, must use ‘var’
10 |
11 | @BeforeEach
12 | fun setup() {
13 | car = TestFactory.car() // Re-initializes property before each test case
14 | }
15 | // ...
16 | }
17 |
--------------------------------------------------------------------------------
/listings/src/main/kotlin/chapter04/Listing0408.kts:
--------------------------------------------------------------------------------
1 | import kotlin.reflect.KProperty
2 | class MyDelegate {
3 | operator fun getValue(cat: Cat, property: KProperty<*>): String = ""
4 | operator fun setValue(cat: Cat, property: KProperty<*>, s: String) {}
5 | }
6 | // -----------
7 |
8 | class Cat {
9 | var name: String by MyDelegate() // Delegates to object of class MyDelegate
10 | }
--------------------------------------------------------------------------------
/listings/src/main/kotlin/chapter04/Listing0409.kts:
--------------------------------------------------------------------------------
1 | import kotlin.properties.ReadWriteProperty
2 | import kotlin.reflect.KProperty
3 |
4 | class Cat {
5 | var name: String by MyDelegate() // Delegates to object of class MyDelegate
6 | }
7 | // ------------
8 |
9 | class MyDelegate : ReadWriteProperty { // Implements ReadWriteProperty
10 | var name: String = "Felix"
11 |
12 | // Delegate must have a getValue method (and setValue for mutable properties)
13 | override operator fun getValue(thisRef: Cat, property: KProperty<*>): String {
14 | println("$thisRef requested ${property.name} from MyDelegate")
15 | return name
16 | }
17 |
18 | override operator fun setValue(thisRef: Cat, property: KProperty<*>, value: String) {
19 | println("$thisRef wants to set ${property.name} to $value via MyDelegate")
20 |
21 | if (value.isNotBlank()) {
22 | this.name = value
23 | }
24 | }
25 | }
26 |
27 | val felix = Cat()
28 | println(felix.name) // "Cat@1c655221 requested name from MyDelegate"
29 | felix.name = "Feli" // "Cat@1c655221 wants to set name to Feli via MyDelegate"
30 | println(felix.name) // "Cat@1c655221 requested name from MyDelegate"
31 |
--------------------------------------------------------------------------------
/listings/src/main/kotlin/chapter04/Listing0410.kts:
--------------------------------------------------------------------------------
1 | import java.time.LocalDate
2 | import java.time.temporal.ChronoUnit
3 |
4 | class Cat(val birthday: LocalDate) {
5 | val age: Int by lazy { // Lazy property, computed on demand only if necessary
6 | println("Computing age...")
7 | ChronoUnit.YEARS.between(birthday, LocalDate.now()).toInt() // Computes age
8 | }
9 | }
10 |
11 | val felix = Cat(LocalDate.of(2013, 10, 27))
12 | println("age = ${felix.age}") // "Computing age...\n age = 5" (run in 2018)
13 | println("age = ${felix.age}") // "age = 5"; returns cached value
14 |
--------------------------------------------------------------------------------
/listings/src/main/kotlin/chapter04/Listing0411.kts:
--------------------------------------------------------------------------------
1 | import java.time.LocalDate
2 | import kotlin.properties.Delegates
3 |
4 | class Cat(private val birthday: LocalDate) {
5 | // …
6 | var mood: Mood by Delegates.observable(Mood.GRUMPY) { // Observable property
7 | property, oldValue, newValue -> // Lambda parameters with old and new value
8 |
9 | println("${property.name} change: $oldValue -> $newValue")
10 |
11 | when (newValue) {
12 | Mood.HUNGRY -> println("Time to feed the cat…")
13 | Mood.SLEEPY -> println("Time to rest…")
14 | Mood.GRUMPY -> println("All as always")
15 | }
16 | }
17 | }
18 |
19 | enum class Mood { GRUMPY, HUNGRY, SLEEPY } // Enums are explained later
20 |
21 | val felix = Cat(LocalDate.of(2013, 11, 27))
22 | felix.mood = Mood.HUNGRY // "mood change: GRUMPY -> HUNGRY\n Time to feed the cat…"
23 | felix.mood = Mood.SLEEPY // "mood change: HUNGRY -> ASLEEP\n Time to rest…"
24 | felix.mood = Mood.GRUMPY // "mood change: ASLEEP -> GRUMPY\n All as always"
25 |
--------------------------------------------------------------------------------
/listings/src/main/kotlin/chapter04/Listing0412.kts:
--------------------------------------------------------------------------------
1 | enum class Mood { GRUMPY, HUNGRY, SLEEPY }
2 | // -------------
3 |
4 | class JsonPerson(properties: Map) {
5 | val name: String by properties // Delegates property to the map
6 | val age: Int by properties // Delegates property to the map
7 | val mood: Mood by properties // Delegates property to the map
8 | }
9 |
10 | // Assume the data comes from JSON; keys in the map must match property names
11 | val jsonData = mutableMapOf("name" to "John Doe", "age" to 42, "mood" to "GRUMPY")
12 |
13 | // You may need to preprocess some data (requires MutableMap)
14 | jsonData["mood"] = Mood.valueOf(jsonData["mood"] as String) // ‘valueOf’ is built-in
15 |
16 | // Creates an object from map
17 | val john = JsonPerson(jsonData) // Properties are matched to the keys in the map
18 | println(john.name) // "John Doe"
19 | println(john.age) // 42
20 | println(john.mood) // GRUMPY
21 |
22 | // Read-only property changes if backing map changes
23 | jsonData["name"] = "Hacker"
24 | println(john.name) // Prints "Hacker"
25 |
--------------------------------------------------------------------------------
/listings/src/main/kotlin/chapter04/Listing0413.kts:
--------------------------------------------------------------------------------
1 | interface Kickable {
2 | fun kick()
3 | }
4 |
5 | // Existing implementation of Kickable
6 | class BaseKickHandler : Kickable {
7 | override fun kick() { println("Got kicked") }
8 | }
9 |
10 | // Football implements interface by delegating to existing impl. (zero boilerplate)
11 | class Football(kickHandler: Kickable) : Kickable by kickHandler
12 |
--------------------------------------------------------------------------------
/listings/src/main/kotlin/chapter04/Listing0414.kts:
--------------------------------------------------------------------------------
1 | interface Kickable {
2 | fun kick()
3 | }
4 | // ------------------
5 |
6 | // Implements interface with manual delegation
7 | class Football(val kickHandler: Kickable) : Kickable {
8 | override fun kick() {
9 | kickHandler.kick() // Trivial forwarding; necessary for every interface method
10 | }
11 | }
12 |
--------------------------------------------------------------------------------
/listings/src/main/kotlin/chapter04/Listing0415.kts:
--------------------------------------------------------------------------------
1 | open class ForwardingMutableSet(set: MutableSet) : MutableSet by set
--------------------------------------------------------------------------------
/listings/src/main/kotlin/chapter04/Listing0416.kts:
--------------------------------------------------------------------------------
1 | class Foo {
2 | fun plainMethod() {}
3 | infix fun with(other: Any) = Pair(this, other)
4 | inline fun inlined(i: Int, operation: (Int, Int) -> Int) = operation(i, 42)
5 | operator fun Int.times(str: String) = str.repeat(this)
6 | fun withDefaults(n: Int = 1, str: String = "Hello World") = n * str
7 | }
8 |
9 | val obj = Foo()
10 | obj.plainMethod()
11 | val pair = obj with "Kotlin"
12 | val foo = obj.inlined(3, { i, j -> i * j }) // 126
13 | obj.withDefaults(str = "Hello Kotlin") // "Hello Kotlin"
14 | with(obj) { 2 * "hi" } // Uses 'with' to access extension
15 |
16 | // -----------
17 | println(foo)
18 | println(obj.withDefaults(str = "Hello Kotlin"))
--------------------------------------------------------------------------------
/listings/src/main/kotlin/chapter04/Listing0417.kts:
--------------------------------------------------------------------------------
1 | class Container {
2 | fun Int.foo() { // Extends Int with a ‘foo’ method
3 | println(toString()) // Calls Int.toString (like this.toString())
4 | println(this@Container.toString()) // Calls Container.toString
5 | }
6 |
7 | fun bar() = 17.foo() // Uses extension method
8 | }
9 |
10 | Container().bar() // "17\n Container@33833882"
11 |
--------------------------------------------------------------------------------
/listings/src/main/kotlin/chapter04/Listing0418.kts:
--------------------------------------------------------------------------------
1 | class Company {
2 | val yearlyRevenue = 10_000_000
3 |
4 | class Nested {
5 | val company = Company() // Must create instance of outer class manually
6 | val revenue = company.yearlyRevenue
7 | }
8 |
9 | inner class Inner {
10 | val revenue = yearlyRevenue // Has access to outer class members automatically
11 | }
12 | }
13 |
--------------------------------------------------------------------------------
/listings/src/main/kotlin/chapter04/Listing0419.kts:
--------------------------------------------------------------------------------
1 | class Task(_title: String, _priority: Int) { // Defines regular parameters
2 | val title = _title.capitalize() // Uses parameter in initializer
3 | var priority: Int
4 |
5 | init {
6 | priority = Math.max(_priority, 0) // Uses parameter in init-block
7 | }
8 | }
9 |
--------------------------------------------------------------------------------
/listings/src/main/kotlin/chapter04/Listing0420.kts:
--------------------------------------------------------------------------------
1 | class Task private constructor(title: String, priority: Int) { }
--------------------------------------------------------------------------------
/listings/src/main/kotlin/chapter04/Listing0421.kts:
--------------------------------------------------------------------------------
1 | class Task(val title: String, var priority: Int) { // Parameters are now properties
2 | init {
3 | require(priority >= 0) // Uses property in init-block
4 | }
5 | }
6 |
--------------------------------------------------------------------------------
/listings/src/main/kotlin/chapter04/Listing0422.kts:
--------------------------------------------------------------------------------
1 | class Person(val name: String)
2 | // ------------
3 |
4 | class Task(val title: String, var priority: Int) { // Primary
5 | constructor(person: Person) : this("Meet with ${person.name}", 50) { // Secondary
6 | println("Created task to meet ${person.name}")
7 | }
8 | }
9 |
--------------------------------------------------------------------------------
/listings/src/main/kotlin/chapter04/Listing0423.kts:
--------------------------------------------------------------------------------
1 | interface Searchable {
2 | fun search() // All implementing classes have this capability
3 | }
4 |
--------------------------------------------------------------------------------
/listings/src/main/kotlin/chapter04/Listing0424.kts:
--------------------------------------------------------------------------------
1 | interface Archivable {
2 | var archiveWithTimeStamp: Boolean // Abstract property
3 | val maxArchiveSize: Long // Property with default impl., thus must be val
4 | get() = -1 // Default implementation returns -1
5 |
6 | fun archive() // Abstract method
7 | fun print() { // Open method with default implementation
8 | val withOrWithout = if (archiveWithTimeStamp) "with" else "without"
9 | val max = if (maxArchiveSize == -1L) "∞" else "$maxArchiveSize"
10 | println("Archiving up to $max entries $withOrWithout time stamp")
11 | }
12 | }
13 |
--------------------------------------------------------------------------------
/listings/src/main/kotlin/chapter04/Listing0425.kts:
--------------------------------------------------------------------------------
1 | import java.io.Serializable
2 |
3 | interface Archivable {
4 | var archiveWithTimeStamp: Boolean // Abstract property
5 | val maxArchiveSize: Long // Property with default impl., thus must be val
6 | get() = -1 // Default implementation returns -1
7 |
8 | fun archive() // Abstract method
9 | fun print() { // Open method with default implementation
10 | val withOrWithout = if (archiveWithTimeStamp) "with" else "without"
11 | val max = if (maxArchiveSize == -1L) "∞" else "$maxArchiveSize"
12 | println("Archiving up to $max entries $withOrWithout time stamp")
13 | }
14 | }
15 | // -----------------
16 |
17 | class Task : Archivable, Serializable { // Implements multiple interfaces
18 | override var archiveWithTimeStamp = true
19 | override fun archive() { }
20 | }
21 |
--------------------------------------------------------------------------------
/listings/src/main/kotlin/chapter04/Listing0426.kts:
--------------------------------------------------------------------------------
1 | interface Archivable {
2 | var archiveWithTimeStamp: Boolean // Abstract property
3 | val maxArchiveSize: Long // Property with default impl., thus must be val
4 | get() = -1 // Default implementation returns -1
5 |
6 | fun archive() // Abstract method
7 | fun print() { // Open method with default implementation
8 | val withOrWithout = if (archiveWithTimeStamp) "with" else "without"
9 | val max = if (maxArchiveSize == -1L) "∞" else "$maxArchiveSize"
10 | println("Archiving up to $max entries $withOrWithout time stamp")
11 | }
12 | }
13 | // -----------------
14 |
15 | abstract class Issue(var priority: Int) {
16 | abstract fun complete() // Abstract method
17 | open fun trivial() { priority = 15 } // Open method
18 | fun escalate() { priority = 100 } // Closed method
19 | }
20 |
21 | class Task(val title: String, priority: Int) : Issue(priority), Archivable {
22 | // … -------
23 | override var archiveWithTimeStamp: Boolean = true
24 | override fun archive() {}
25 | // ---------
26 | override fun complete() { println("Completed task: $title") } // Required override
27 | override fun trivial() { priority = 20 } // Optional override
28 | // Cannot override ‘escalate’ because it is closed
29 | }
30 |
--------------------------------------------------------------------------------
/listings/src/main/kotlin/chapter04/Listing0427.kts:
--------------------------------------------------------------------------------
1 | class Closed // Closed class: cannot inherit from this
2 | //class ChildOfClosed : Closed() // NOT allowed: compile-time error
3 |
4 | open class Open // Open class: can inherit
5 | class ChildOfOpen : Open() // Allowed
6 | //class ChildOfChild : ChildOfOpen() // NOT allowed: compile-time error
7 |
--------------------------------------------------------------------------------
/listings/src/main/kotlin/chapter04/Listing0428.kts:
--------------------------------------------------------------------------------
1 | abstract class Component
2 | class Composite : Component()
3 | class Leaf : Component()
4 | // ----------------
5 |
6 | val item: Component = Leaf() // ‘item’ is a LeafGeneric at runtime
7 |
8 | if (item is Composite) { } // Checks if ‘item’ is of type Composite at runtime
9 | if (item !is Composite) { } // Checks if ‘item’ is not a Composite at runtime
10 |
--------------------------------------------------------------------------------
/listings/src/main/kotlin/chapter04/Listing0429.kts:
--------------------------------------------------------------------------------
1 | abstract class Component
2 | class Composite : Component()
3 | class Leaf : Component()
4 | // ----------------
5 |
6 | val item: Component? = null
7 |
8 | val leaf: MyLeaf = item as MyLeaf // TypeCastException
9 | val leafOrNull: MyLeaf? = item as MyLeaf? // Evaluates to null
10 | val leafSafe: MyLeaf? = item as? MyLeaf // Evaluates to null
11 | val leafNonNull: MyLeaf = (item as? MyLeaf) ?: MyLeaf() // Alternative using elvis op.
12 |
--------------------------------------------------------------------------------
/listings/src/main/kotlin/chapter04/Listing0430.kts:
--------------------------------------------------------------------------------
1 | abstract class Component
2 | class Composite : Component()
3 | class Leaf : Component()
4 | // ----------------
5 |
6 | val composite: Component = Composite()
7 |
8 | val leafOrNull: MyLeaf? = composite as MyLeaf? // ClassCastException
9 | val leafSafe: MyLeaf? = composite as? MyLeaf // Evaluates to null
10 |
--------------------------------------------------------------------------------
/listings/src/main/kotlin/chapter04/Listing0431.kt:
--------------------------------------------------------------------------------
1 | // Entities are renamed to avoid name clashes
2 | abstract class MyComponent { fun component() {} }
3 | class MyComposite : MyComponent() { fun composite() = 1 }
4 | class MyLeaf : MyComponent() { fun leaf() = 2 }
5 | // ----------------
6 |
7 | fun main() {
8 | val comp: MyComponent? = MyLeaf() // Type is nullable ‘MyComponent?’
9 |
10 | if (comp != null) { comp.component() } // Smart-cast to MyComponent
11 | if (comp is MyLeaf) { comp.leaf() } // Smart-cast to LeafGeneric
12 | when (comp) {
13 | is MyComposite -> comp.composite() // Smart-cast to MyComponent
14 | is MyLeaf -> comp.leaf() // Smart-cast to LeafGeneric
15 | }
16 | if (comp is MyComposite && comp.composite() == 16) {} // Smart-cast to MyComposite
17 | if (comp !is MyLeaf || comp.leaf() == 43) {} // Smart-cast to LeafGeneric inside condition
18 | if (comp !is MyComposite) return // Does not work in KTS scripts
19 | comp.composite() // Smart-cast to MyComposite (because of return above)
20 | }
--------------------------------------------------------------------------------
/listings/src/main/kotlin/chapter04/Listing0432.kts:
--------------------------------------------------------------------------------
1 | open class Parent {
2 | val a = "public"
3 | internal val b = "internal"
4 | protected val c = "protected"
5 | private val d = "private"
6 |
7 | inner class Inner {
8 | val accessible = "$a, $b, $c, $d" // All accessible
9 | }
10 | }
11 |
12 | class Child : Parent() {
13 | val accessible = "$a, $b, $c" // d not accessible because private
14 | }
15 |
16 | class Unrelated {
17 | val p = Parent()
18 | val accessible = "${p.a}, ${p.b}" // p.c, p.d not accessible
19 | }
20 |
--------------------------------------------------------------------------------
/listings/src/main/kotlin/chapter04/Listing0433.kts:
--------------------------------------------------------------------------------
1 | open class Cache private constructor() {
2 | val INSTANCE = Cache()
3 |
4 | protected var size: Long = 4096 // Getter inherits visibility from property
5 | private set // Setter can have a different visibility
6 | }
7 |
--------------------------------------------------------------------------------
/listings/src/main/kotlin/chapter04/Listing0434.kts:
--------------------------------------------------------------------------------
1 | data class Contact(val name: String, val phone: String, var favorite: Boolean)
--------------------------------------------------------------------------------
/listings/src/main/kotlin/chapter04/Listing0435.kts:
--------------------------------------------------------------------------------
1 | data class Contact(val name: String, val phone: String, var favorite: Boolean)
2 | // --------------
3 |
4 | val john = Contact("John", "202-555-0123", true)
5 | val john2 = Contact("John", "202-555-0123", true)
6 | val jack = Contact("Jack", "202-555-0789", false)
7 |
8 | // toString
9 | println(jack) // Contact(name=Jack, phone=202-555-0789, favorite=false)
10 |
11 | // equals
12 | println(john == john2) // true
13 | println(john == jack) // false
14 |
15 | // hashCode
16 | val contacts = hashSetOf(john, jack, john2)
17 | println(contacts.size) // 2 (no duplicates in sets, uses hashCode)
18 |
19 | // componentN
20 | val (name, phone, _) = john // Uses destructuring declaration
21 | println("$name's number is $phone") // John's number is 202-555-0123
22 |
23 | // copy
24 | val johnsSister = john.copy(name = "Joanne")
25 | println(johnsSister) // Contact(name=Joanne, phone=202-555-0123, favorite=true)
26 |
--------------------------------------------------------------------------------
/listings/src/main/kotlin/chapter04/Listing0436.kts:
--------------------------------------------------------------------------------
1 | enum class PaymentStatus {
2 | OPEN, PAID
3 | }
4 |
--------------------------------------------------------------------------------
/listings/src/main/kotlin/chapter04/Listing0437.kts:
--------------------------------------------------------------------------------
1 | enum class PaymentStatus(val billable: Boolean) { // Enum with a property
2 |
3 | OPEN(true) {
4 | override fun calculate() { }
5 | },
6 | PAID(false) {
7 | override fun calculate() { }
8 | }; // Note the semicolon: it separates the enum instances from the members
9 |
10 | fun print() { println("Payment is ${this.name}") } // Concrete method
11 | abstract fun calculate() // Abstract method
12 | }
13 |
--------------------------------------------------------------------------------
/listings/src/main/kotlin/chapter04/Listing0438.kts:
--------------------------------------------------------------------------------
1 | enum class PaymentStatus(val billable: Boolean) { // Enum with a property
2 |
3 | OPEN(true) {
4 | override fun calculate() { }
5 | },
6 | PAID(false) {
7 | override fun calculate() { }
8 | }; // Note the semicolon: it separates the enum instances from the members
9 |
10 | fun print() { println("Payment is ${this.name}") } // Concrete method
11 | abstract fun calculate() // Abstract method
12 | }
13 | // -------------
14 |
15 | val status = PaymentStatus.PAID
16 | status.print() // Prints "Payment is PAID"
17 | status.calculate()
18 |
19 | // Kotlin generates ‘name’ and ‘ordinal’ properties
20 | println(status.name) // PAID
21 | println(status.ordinal) // 1
22 | println(status.billable) // false
23 |
24 | // Enum instances implement Comparable (order = order of declaration)
25 | println(PaymentStatus.PAID > PaymentStatus.OPEN) // true
26 |
27 | // ‘values’ gets all possible enum values
28 | var values = PaymentStatus.values()
29 | println(values.joinToString()) // OPEN, PAID
30 |
31 | // ‘valueOf’ retrieves an enum instance by name
32 | println(PaymentStatus.valueOf("OPEN")) // OPEN
33 |
--------------------------------------------------------------------------------
/listings/src/main/kotlin/chapter04/Listing0439.kts:
--------------------------------------------------------------------------------
1 | enum class PaymentStatus(val billable: Boolean) { // Enum with a property
2 |
3 | OPEN(true) {
4 | override fun calculate() { }
5 | },
6 | PAID(false) {
7 | override fun calculate() { }
8 | }; // Note the semicolon: it separates the enum instances from the members
9 |
10 | fun print() { println("Payment is ${this.name}") } // Concrete method
11 | abstract fun calculate() // Abstract method
12 | }
13 | // -------------
14 |
15 | val status = PaymentStatus.OPEN
16 | val message = when(status) {
17 | PaymentStatus.PAID -> "Thanks for your payment!"
18 | PaymentStatus.OPEN -> "Please pay your bill so we can buy coffee."
19 | } // No else-branch necessary
20 |
--------------------------------------------------------------------------------
/listings/src/main/kotlin/chapter04/Listing0440.kt:
--------------------------------------------------------------------------------
1 | sealed class BinaryTree // Sealed class with two direct subclasses (and no others)
2 | data class Leaf(val value: Int) : BinaryTree()
3 | data class Branch(val left: BinaryTree, val right: BinaryTree) : BinaryTree()
4 | // -------------
5 |
6 | fun main() {
7 | // Creates a binary tree object
8 | val tree: BinaryTree = Branch(Branch(Leaf(1), Branch(Leaf(2), Leaf(3))), Leaf(4))
9 | println(tree)
10 | }
--------------------------------------------------------------------------------
/listings/src/main/kotlin/chapter04/Listing0441.kt:
--------------------------------------------------------------------------------
1 | sealed class Expr // Sealed class representing possible arithmetic expressions
2 | data class Const (val value: Int) : Expr()
3 | data class Plus (val left: Expr, val right: Expr) : Expr()
4 | data class Minus (val left: Expr, val right: Expr) : Expr()
5 | data class Times (val left: Expr, val right: Expr) : Expr()
6 | data class Divide(val left: Expr, val right: Expr) : Expr()
7 |
8 | fun evaluate(expr: Expr): Double = when(expr) {
9 | is Const -> expr.value.toDouble()
10 | is Plus -> evaluate(expr.left) + evaluate(expr.right)
11 | is Minus -> evaluate(expr.left) - evaluate(expr.right)
12 | is Times -> evaluate(expr.left) * evaluate(expr.right)
13 | is Divide -> evaluate(expr.left) / evaluate(expr.right)
14 | } // No else-branch required: all possible cases are handled
15 |
16 | val formula: Expr = Times(Plus(Const(2), Const(4)), Minus(Const(8), Const(1)))
17 |
18 | // ------- (need main here)
19 | fun main() {
20 | println(evaluate(formula)) // 42.0
21 | }
22 |
23 |
--------------------------------------------------------------------------------
/listings/src/main/kotlin/chapter04/Listing0442.kts:
--------------------------------------------------------------------------------
1 | fun areaOfEllipse(vertex: Double, covertex: Double): Double {
2 | val ellipse = object { // Ad-hoc object
3 | val x = vertex
4 | val y = covertex
5 | }
6 | return Math.PI * ellipse.x * ellipse.y
7 | }
8 |
--------------------------------------------------------------------------------
/listings/src/main/kotlin/chapter04/Listing0443.kts:
--------------------------------------------------------------------------------
1 | // Android-specific example (requires Android context to compile and run)
2 |
3 | //scrollView.setOnDragListener(object : View.OnDragListener {
4 | // override fun onDrag(view: View, event: DragEvent): Boolean {
5 | // Log.d(TAG, "Dragging...")
6 | // return true
7 | // }
8 | //})
--------------------------------------------------------------------------------
/listings/src/main/kotlin/chapter04/Listing0444.kts:
--------------------------------------------------------------------------------
1 | class ReturnsObjectExpressions {
2 | fun prop() = object { // Returns an object; infers Any type
3 | val prop = "Not accessible"
4 | }
5 |
6 | fun propWithInterface() = object : HasProp { // Returns an object; infers HasProp
7 | override val prop = "Accessible"
8 | }
9 |
10 | fun access() {
11 | // prop().prop // Compile-time error (Any does not have prop)
12 | propWithInterface().prop // Now possible (HasProp has prop)
13 | }
14 | }
15 |
16 | interface HasProp { // Allows exposing the ‘prop’ to the outside
17 | val prop: String
18 | }
19 |
--------------------------------------------------------------------------------
/listings/src/main/kotlin/chapter04/Listing0445.kts:
--------------------------------------------------------------------------------
1 | import javafx.scene.Scene
2 |
3 | object SceneRegistry { // Declares an object: this is effectively a Singleton
4 | lateinit private var homeScene: Scene
5 | lateinit private var settingsScene: Scene
6 | fun buildHomeScene() { }
7 | fun buildSettingsScene() { }
8 | }
9 |
--------------------------------------------------------------------------------
/listings/src/main/kotlin/chapter04/Listing0446.kts:
--------------------------------------------------------------------------------
1 | import javafx.scene.Scene
2 |
3 | object SceneRegistry { // Declares an object: this is effectively a Singleton
4 | lateinit private var homeScene: Scene
5 | lateinit private var settingsScene: Scene
6 | fun buildHomeScene() { }
7 | fun buildSettingsScene() { }
8 | }
9 | // ----------
10 |
11 | val home = SceneRegistry.buildHomeScene()
12 | val settings = SceneRegistry.buildSettingsScene()
13 |
--------------------------------------------------------------------------------
/listings/src/main/kotlin/chapter04/Listing0447.kts:
--------------------------------------------------------------------------------
1 | data class Car(val model: String, val maxSpeed: Int) {
2 | companion object Factory {
3 | fun defaultCar() = Car("SuperCar XY", 360)
4 | }
5 | }
6 |
7 | val car = Car.defaultCar() // Calls companion method: same as Car.Factory.defaultCar()
8 |
--------------------------------------------------------------------------------
/listings/src/main/kotlin/chapter04/Listing0448.kts:
--------------------------------------------------------------------------------
1 | interface CarFactory {
2 | fun defaultCar(): Car
3 | }
4 |
5 | data class Car(val model: String, val maxSpeed: Int) {
6 | companion object Factory : CarFactory { // Objects can implement interfaces
7 | override fun defaultCar() = Car("SuperCar XY", 360)
8 | }
9 | }
10 |
--------------------------------------------------------------------------------
/listings/src/main/kotlin/chapter04/Listing0449.kts:
--------------------------------------------------------------------------------
1 | class Box(elem: T) { // Generic class: can be a box of integers, box of strings, …
2 | val element: T = elem
3 | }
4 |
5 | val box: Box = Box(17) // Type Box can also be inferred
6 | println(box.element) // 17
7 |
--------------------------------------------------------------------------------
/listings/src/main/kotlin/chapter04/Listing0450.kt:
--------------------------------------------------------------------------------
1 | // Suffix "Generic" is only to avoid name clashes inside project
2 | sealed class BinaryTreeGeneric // Now generic: can carry values of any type
3 | data class LeafGeneric(val value: T) : BinaryTreeGeneric()
4 | data class BranchGeneric(
5 | val left: BinaryTreeGeneric,
6 | val right: BinaryTreeGeneric
7 | ) : BinaryTreeGeneric()
8 |
9 | val tree: BinaryTreeGeneric = LeafGeneric(3.1415) // Uses a binary tree with Double values
10 |
--------------------------------------------------------------------------------
/listings/src/main/kotlin/chapter04/Listing0451.kts:
--------------------------------------------------------------------------------
1 | import java.util.*
2 |
3 | fun myListOf(vararg elements: T) = Arrays.asList(*elements) // * is spread operator
4 |
5 | val list0: List = myListOf(1, 2, 3) // All types explicit, no type inference
6 | val list1: List = myListOf(1, 2, 3) // Infers myListOf
7 | val list2 = myListOf(1, 2, 3) // Infers MutableList and myListOf
8 | val list3 = myListOf(1, 2, 3) // Infers MutableList
9 |
10 | // Without parameters
11 | val list4 = myListOf() // Infers MutableList
12 | val list5: List = myListOf() // Infers myListOf
13 |
--------------------------------------------------------------------------------
/listings/src/main/kotlin/chapter04/Listing0452.kts:
--------------------------------------------------------------------------------
1 | fun Iterable<*>.filterByType(clazz: Class): List {
2 | @Suppress("UNCHECKED_CAST") // Must suppress unchecked cast
3 | return this.filter { clazz.isInstance(it) }.map { it as T? } // Uses reflection
4 | }
5 |
6 | val elements = listOf(4, 5.6, "hello", 6, "hi", null, 1)
7 | println(elements.filterByType(Int::class.javaObjectType)) // 4, 6, 1
8 |
--------------------------------------------------------------------------------
/listings/src/main/kotlin/chapter04/Listing0453.kts:
--------------------------------------------------------------------------------
1 | inline fun Iterable<*>.filterByType(): List { // Inline+reified
2 | return this.filter { it is T }.map { it as T? } // No reflection used, can use ‘is’
3 | }
4 |
5 | val elements = listOf(4, 5.6, "hello", 6, "hi", null, 1)
6 | println(elements.filterByType()) // 4, 6, 1
7 |
--------------------------------------------------------------------------------
/listings/src/main/kotlin/chapter04/Listing0454.kts:
--------------------------------------------------------------------------------
1 | val elements = listOf(4, 5.6, "hello", 6, "hi", null, 1)
2 | // ---------------
3 |
4 | elements.filter { it is Int }.map { it as Int? } // Inlined function and inserted T
--------------------------------------------------------------------------------
/listings/src/main/kotlin/chapter04/Listing0455.kts:
--------------------------------------------------------------------------------
1 | import java.io.Serializable
2 |
3 | interface Archivable {
4 | var archiveWithTimeStamp: Boolean // Abstract property
5 | val maxArchiveSize: Long // Property with default impl., thus must be val
6 | get() = -1 // Default implementation returns -1
7 | fun archive() // Abstract method
8 | fun print() { // Open method with default implementation
9 | val withOrWithout = if (archiveWithTimeStamp) "with" else "without"
10 | val max = if (maxArchiveSize == -1L) "∞" else "$maxArchiveSize"
11 | println("Archiving up to $max entries $withOrWithout time stamp")
12 | }
13 | }
14 |
15 | class Task(title: String, priority: Int) : Archivable, Serializable { // Implements multiple interfaces
16 | override var archiveWithTimeStamp = true
17 | override fun archive() { }
18 | }
19 | // -----------------
20 |
21 | val n: Number = 3 // Integer is a subclass of Number
22 | val todo: Archivable = Task("Write book", 99) // Task is a subclass of Archivable
23 |
--------------------------------------------------------------------------------
/listings/src/main/kotlin/chapter04/Listing0456.kts:
--------------------------------------------------------------------------------
1 | import java.time.LocalDate
2 | import java.time.temporal.ChronoUnit
3 |
4 | abstract class Human(val birthday: LocalDate) {
5 | abstract fun age(): Number // Specifies that method returns a Number
6 | }
7 |
8 | class Student(birthday: LocalDate) : Human(birthday) {
9 | // Return types allow variance so that Int can be used in place of Number
10 | override fun age(): Int = ChronoUnit.YEARS.between(birthday, LocalDate.now()).toInt()
11 | }
12 |
--------------------------------------------------------------------------------
/listings/src/main/kotlin/chapter04/Listing0456JavaNote.java:
--------------------------------------------------------------------------------
1 | package chapter04;
2 |
3 | class UnsafeArrays {
4 | public static void main(String[] args) {
5 | Number[] arr = new Integer[3]; // Arrays are covariant
6 | arr[0] = 3.7; // Causes ArrayStoreException
7 | }
8 | }
9 |
--------------------------------------------------------------------------------
/listings/src/main/kotlin/chapter04/Listing0457.kts:
--------------------------------------------------------------------------------
1 | //val arr: Array = arrayOf(1, 2, 3) // Compile-time error
2 | //arr[0] = 3.1415 // Would be unsafe (but cannot happen)
--------------------------------------------------------------------------------
/listings/src/main/kotlin/chapter04/Listing0458.java:
--------------------------------------------------------------------------------
1 | package chapter04;
2 |
3 | class CollectionsInvariant {
4 | public static void main(String[] args) {
5 | // List numbers = new ArrayList(); // Compile-time error
6 | }
7 | }
--------------------------------------------------------------------------------
/listings/src/main/kotlin/chapter04/Listing0459.kts:
--------------------------------------------------------------------------------
1 | val numbers: List = listOf(1, 2, 3) // Works
--------------------------------------------------------------------------------
/listings/src/main/kotlin/chapter04/Listing0460.kts:
--------------------------------------------------------------------------------
1 | //val numbers: MutableList = mutableListOf(1, 2, 3) // Compile-time error
--------------------------------------------------------------------------------
/listings/src/main/kotlin/chapter04/Listing0461.kts:
--------------------------------------------------------------------------------
1 | open class Stack(vararg elements: E) { // ‘out’ indicates covariance
2 | protected open val elements: List = elements.toList() // E used in out-position
3 | fun peek(): E = elements.last() // E used in out-position
4 | fun size() = elements.size
5 | }
6 |
7 | val stack: Stack = Stack(1, 2, 3) // Allowed because covariant
8 |
--------------------------------------------------------------------------------
/listings/src/main/kotlin/chapter04/Listing0462.kts:
--------------------------------------------------------------------------------
1 | open class Stack(vararg elements: E) { // ‘out’ indicates covariance
2 | protected open val elements: List = elements.toList() // E used in out-position
3 | fun peek(): E = elements.last() // E used in out-position
4 | fun size() = elements.size
5 | }
6 | // ----------
7 |
8 | class MutableStack(vararg elements: E) : Stack() { // Mutable: must be invariant
9 | override val elements: MutableList = elements.toMutableList()
10 | fun push(element: E) = elements.add(element)
11 | fun pop() = elements.removeAt(elements.size - 1)
12 | }
13 |
14 | //val mutable: MutableStack = MutableStack(1, 2, 3) // Compile-time error
15 |
--------------------------------------------------------------------------------
/listings/src/main/kotlin/chapter04/Listing0463.kts:
--------------------------------------------------------------------------------
1 | interface Compare { // ‘in’ indicates contravariance
2 | fun compare(a: T, b: T): Int // T is only used at in-position
3 | }
4 |
--------------------------------------------------------------------------------
/listings/src/main/kotlin/chapter04/Listing0464.kts:
--------------------------------------------------------------------------------
1 | interface Compare { // ‘in’ indicates contravariance
2 | fun compare(a: T, b: T): Int // T is only used at in-position
3 | }
4 |
5 | abstract class Issue(var priority: Int) {
6 | abstract fun complete() // Abstract method
7 | open fun trivial() { priority = 15 } // Open method
8 | fun escalate() { priority = 100 } // Closed method
9 | }
10 | class Task(val title: String, priority: Int) : Issue(priority) {
11 | override fun complete() { println("Completed task: $title") } // Required override
12 | }
13 | // -----------
14 |
15 | val taskComparator: Compare = object : Compare { // Uses contravariance
16 | override fun compare(a: Issue, b: Issue) = a.priority - b.priority
17 | }
18 |
--------------------------------------------------------------------------------
/listings/src/main/kotlin/chapter04/Listing0465.kts:
--------------------------------------------------------------------------------
1 | interface Repair { // Contravariant
2 | fun repair(t: T) // T in in-position
3 | }
4 |
5 | open class Vehicle(var damaged: Boolean)
6 | class Bike(damaged: Boolean) : Vehicle(damaged)
7 |
8 | class AllroundRepair : Repair {
9 | override fun repair(vehicle: Vehicle) {
10 | vehicle.damaged = false
11 | }
12 | }
13 |
14 | val bikeRepair: Repair = AllroundRepair() // Uses contravariance
15 |
--------------------------------------------------------------------------------
/listings/src/main/kotlin/chapter04/Listing0466.kts:
--------------------------------------------------------------------------------
1 | //val normal: MutableList = mutableListOf(1, 2, 3) // Compile-time error
2 | val producer: MutableList = mutableListOf(1, 2, 3) // Now covariant
3 |
4 | // Can only read from producer, not write to it
5 | //producer.add(???) // Parameter type is Nothing, so impossible to call add()
6 | val n: Number = producer[0] // Returns Number
7 |
--------------------------------------------------------------------------------
/listings/src/main/kotlin/chapter04/Listing0467.kts:
--------------------------------------------------------------------------------
1 | //val normal: MutableList = mutableListOf(1.7, 2f, 3) // Compile-time error
2 | val consumer: MutableList = mutableListOf(1.7, 2f, 3) // Contravariant
3 |
4 | // Can write to consumer normally, but only read values as Any?
5 | consumer.add(4)
6 | val value: Any? = consumer[0] // Return type is Any? (only type-safe choice)
7 |
--------------------------------------------------------------------------------
/listings/src/main/kotlin/chapter04/Listing0468.kts:
--------------------------------------------------------------------------------
1 | open class Stack(vararg elements: E) { // ‘out’ indicates covariance
2 | protected open val elements: List = elements.toList() // E used in out-position
3 | fun peek(): E = elements.last() // E used in out-position
4 | fun size() = elements.size
5 | }
6 |
7 | class MutableStack(vararg elements: E) : Stack() { // Mutable: must be invariant
8 | override val elements: MutableList = elements.toMutableList()
9 | fun push(element: E) = elements.add(element)
10 | fun pop() = elements.removeAt(elements.size - 1)
11 | }
12 | // ----------
13 |
14 | fun transfer(from: MutableStack, to: MutableStack) {
15 | while (from.size() > 0) { to.push(from.pop()) }
16 | }
17 |
18 | val from = MutableStack(1, 2, 3)
19 | val to = MutableStack(9.87, 42, 1.23)
20 | //transfer(from, to) // Compile-time error, although it would be safe
21 |
--------------------------------------------------------------------------------
/listings/src/main/kotlin/chapter04/Listing0469.kts:
--------------------------------------------------------------------------------
1 | open class Stack(vararg elements: E) { // ‘out’ indicates covariance
2 | protected open val elements: List = elements.toList() // E used in out-position
3 | fun peek(): E = elements.last() // E used in out-position
4 | fun size() = elements.size
5 | }
6 |
7 | class MutableStack(vararg elements: E) : Stack() { // Mutable: must be invariant
8 | override val elements: MutableList = elements.toMutableList()
9 | fun push(element: E) = elements.add(element)
10 | fun pop() = elements.removeAt(elements.size - 1)
11 | }
12 | // ----------
13 |
14 | fun transfer(from: MutableStack, to: MutableStack) {
15 | // Same as before
16 | while (from.size() > 0) { to.push(from.pop()) }
17 | }
18 |
19 | val from = MutableStack(1, 2, 3)
20 | val to = MutableStack(9.87, 42, 1.23)
21 | transfer(from, to) // Works now due to variance
22 |
--------------------------------------------------------------------------------
/listings/src/main/kotlin/chapter04/Listing0470.kts:
--------------------------------------------------------------------------------
1 | open class Vehicle(var damaged: Boolean)
2 | // ------------
3 |
4 | interface Repair { // Upper bound for T is Vehicle
5 | fun repair(t: T)
6 | }
7 |
--------------------------------------------------------------------------------
/listings/src/main/kotlin/chapter04/Listing0471.kts:
--------------------------------------------------------------------------------
1 | open class Vehicle(var damaged: Boolean)
2 | // ----------
3 |
4 | interface Repairable
5 |
6 | interface Repair
7 | where T : Vehicle, // where-clause lists all type bounds
8 | T : Repairable {
9 | fun repair(t: T)
10 | }
11 |
--------------------------------------------------------------------------------
/listings/src/main/kotlin/chapter04/Listing0472.kts:
--------------------------------------------------------------------------------
1 | class Person(val name: String)
2 | // -----------
3 |
4 | fun printAll(array: Array<*>) { // Nothing known about type of array, but type-safe
5 | array.forEach(::println)
6 | }
7 |
8 | printAll(arrayOf(1, 2, 3))
9 | printAll(arrayOf("a", 2.7, Person("Sandy"))) // Can pass in arrays of arbitrary type
10 |
--------------------------------------------------------------------------------
/listings/src/main/kotlin/chapter04/Listing0473.kts:
--------------------------------------------------------------------------------
1 | abstract class Issue(var priority: Int) {
2 | abstract fun complete() // Abstract method
3 | open fun trivial() { priority = 15 } // Open method
4 | fun escalate() { priority = 100 } // Closed method
5 | }
6 |
7 | class Task(val title: String, priority: Int) : Issue(priority) {
8 | override fun complete() { println("Completed task: $title") } // Required override
9 | override fun trivial() { priority = 20 } // Optional override
10 | // Cannot override ‘escalate’ because it is closed
11 | }
12 | // ------------
13 |
14 | interface Function { // Contravariant w.r.t. T and covariant w.r.t. R
15 | fun apply(t: T): R
16 | }
17 |
18 | object IssueToInt : Function {
19 | override fun apply(t: Issue) = t.priority
20 | }
21 |
22 | // Star projections
23 | val f1: Function<*, Int> = IssueToInt // apply() not callable but would return Int
24 | val f2: Function = IssueToInt // apply() callable, returns Any?
25 | val f3: Function<*, *> = IssueToInt // apply() not callable and would return Any?
26 |
27 | val errand = Task("Save the world", 80)
28 | val urgency: Any? = f2.apply(errand)
29 |
--------------------------------------------------------------------------------
/listings/src/main/kotlin/chapter05/Listing0501.kts:
--------------------------------------------------------------------------------
1 | import com.google.common.math.Stats
2 | import java.util.*
3 |
4 | // Using the standard library
5 | val arrayList = ArrayList() // Uses java.util.ArrayList
6 | arrayList.addAll(arrayOf("Mercury", "Venus", "Jupiter"))
7 | arrayList[2] = arrayList[0] // Indexed access operator calls ‘get’ and ‘set’
8 |
9 | // Looping as usual, ArrayList provides iterator()
10 | for (item in arrayList) { println(item) }
11 |
12 | // Using a third-party library (Google Guava)
13 | val stats = Stats.of(4, 8, 15, 16, 23, 42)
14 | println(stats.sum()) // 108
15 |
--------------------------------------------------------------------------------
/listings/src/main/kotlin/chapter05/Listing0502.kts:
--------------------------------------------------------------------------------
1 | import chapter05.GettersAndSetters
2 |
3 | val gs = GettersAndSetters()
4 | println(gs.readOnly) // Read-only attribute acts like a val property
5 |
6 | gs.readWrite = "I have both" // Read-write attribute acts like a var property
7 | println(gs.readWrite)
8 |
9 | gs.setWriteOnly("No getter") // Write-only properties not supported in Kotlin
--------------------------------------------------------------------------------
/listings/src/main/kotlin/chapter05/Listing0503.kts:
--------------------------------------------------------------------------------
1 | import chapter05.Listing0503.hello
2 |
3 | val str = hello() // Inferred type: String (by default)
4 | println(str.length)
5 |
6 | val nullable: String? = hello() // Explicit type: String?
7 | println(nullable?.length)
8 |
--------------------------------------------------------------------------------
/listings/src/main/kotlin/chapter05/Listing0504.kts:
--------------------------------------------------------------------------------
1 | import chapter05.Listing0504.nonNull
2 | import chapter05.Listing0504.nullable
3 |
4 | // --------------
5 | val s1 = nullable() // Inferred type: String?
6 | val s2 = nonNull() // Inferred type: String
7 |
--------------------------------------------------------------------------------
/listings/src/main/kotlin/chapter05/Listing0505.kts:
--------------------------------------------------------------------------------
1 | import chapter05.KeywordsAsIdentifiers
2 |
3 | val kai = KeywordsAsIdentifiers()
4 | println(kai.`val`)
5 | println(kai.`object`)
6 | println(kai.`in`(listOf(1, 2, 3)))
7 | kai.`fun`()
--------------------------------------------------------------------------------
/listings/src/main/kotlin/chapter05/Listing0506.kts:
--------------------------------------------------------------------------------
1 | import chapter05.Listing0506.myListOf
2 |
3 | val list = myListOf("a", "b", "c") // Can pass in any number of args
4 | val values = arrayOf("d", "e", "f")
5 | val list2 = myListOf(*values) // Spread operator required
6 |
--------------------------------------------------------------------------------
/listings/src/main/kotlin/chapter05/Listing0507.kts:
--------------------------------------------------------------------------------
1 | import chapter05.Box
2 |
3 | val value1 = Box(19)
4 | val value2 = Box(37)
5 | val value3 = Box(14)
6 |
7 | val result = value1 + value2 - value3 // Uses ‘plus’ and ‘minus’ as operators
8 | println(result.value) // 42
9 |
--------------------------------------------------------------------------------
/listings/src/main/kotlin/chapter05/Listing0508.kts:
--------------------------------------------------------------------------------
1 | package chapter05
2 |
3 | private val creator = Producer { Box(9000) } // Inferred type: Producer
4 |
5 | println(creator.produce().value) // 9000
--------------------------------------------------------------------------------
/listings/src/main/kotlin/chapter05/Listing0509.kt:
--------------------------------------------------------------------------------
1 | val thread = Thread { // No need to write Thread(Runnable { … })
2 | println("I'm the runnable")
3 | }
4 |
--------------------------------------------------------------------------------
/listings/src/main/kotlin/chapter05/Listing0510.kt:
--------------------------------------------------------------------------------
1 | package chapter05
2 |
3 | class KotlinClass {
4 | val fixed: String = "base.KotlinClass"
5 | var mutable: Boolean = false
6 | }
7 |
--------------------------------------------------------------------------------
/listings/src/main/kotlin/chapter05/Listing0511.kt:
--------------------------------------------------------------------------------
1 | package chapter05
2 |
3 | // Must rename class to avoid name clashes in project
4 | class KotlinClass0511 {
5 | var mutable: Boolean = false
6 | @JvmName("isMutable") get // Specifies custom getter name for Java bytecode
7 | }
8 |
--------------------------------------------------------------------------------
/listings/src/main/kotlin/chapter05/Listing0512.kt:
--------------------------------------------------------------------------------
1 | val prop = "Default: private field + getter/setter" // Here no setter because read-only
2 |
3 | @JvmField
4 | val exposed = "Exposed as a field in Java" // No getter or setter generated
5 |
--------------------------------------------------------------------------------
/listings/src/main/kotlin/chapter05/Listing0513.kt:
--------------------------------------------------------------------------------
1 | class FileLevelClass
2 | object FileLevelObject
3 | fun fileLevelFunction() {}
4 | val fileLevelVariable = "Usable from Java"
5 |
--------------------------------------------------------------------------------
/listings/src/main/kotlin/chapter05/Listing0514.kt:
--------------------------------------------------------------------------------
1 | @file:JvmName("MyUtils")
2 |
3 | // Must rename declarations to avoid name clashes
4 | class FileLevelClass0514
5 | object FileLevelObject0514
6 | fun fileLevelFunction0514() {}
7 | val fileLevelVariable0514 = "Usable from Java"
8 |
--------------------------------------------------------------------------------
/listings/src/main/kotlin/chapter05/Listing0515.kt:
--------------------------------------------------------------------------------
1 | @file:JvmName("Notifications")
2 |
3 | // Android-specific example
4 |
5 | //import android.content.Context
6 | //import android.widget.Toast
7 | //
8 | //fun Context.toast(message: String) { // Top-level function => becomes static method
9 | // Toast.makeText(this, message, Toast.LENGTH_SHORT).show()
10 | //}
11 |
--------------------------------------------------------------------------------
/listings/src/main/kotlin/chapter05/Listing0516.kt:
--------------------------------------------------------------------------------
1 | // Android-specific example
2 |
3 | //import android.content.Context
4 | //import android.widget.Toast
5 | //
6 | //class Notifier {
7 | // fun Context.longToast(message: String) { // Becomes member method
8 | // Toast.makeText(this, message, Toast.LENGTH_LONG).show()
9 | // }
10 | //}
11 |
--------------------------------------------------------------------------------
/listings/src/main/kotlin/chapter05/Listing0517.kt:
--------------------------------------------------------------------------------
1 | package chapter05
2 |
3 | const val CONSTANT = 360
4 |
5 | object Cache { val obj = "Expensive object here..." }
6 |
7 | class Car {
8 | companion object Factory { val defaultCar = Car() }
9 | }
10 |
--------------------------------------------------------------------------------
/listings/src/main/kotlin/chapter05/Listing0518.kt:
--------------------------------------------------------------------------------
1 | package chapter05
2 |
3 | // Must rename declarations to avoid name clashes in project
4 | object Cache0518 {
5 | @JvmStatic fun cache(key: String, obj: Any) { } // Becomes a static member
6 | }
7 |
8 | class Car0518 {
9 | companion object Factory {
10 | @JvmStatic fun produceCar() { } // Now becomes static as well
11 | }
12 | }
13 |
--------------------------------------------------------------------------------
/listings/src/main/kotlin/chapter05/Listing0519.kt:
--------------------------------------------------------------------------------
1 | @file:JvmName("ArrayUtils")
2 | package chapter05
3 |
4 | @JvmOverloads // Triggers generation of overloaded methods in Java bytecode
5 | fun Array.join(delimiter: String = ", ",
6 | prefix: String = "",
7 | suffix: String = ""): String {
8 | return this.joinToString(delimiter, prefix, suffix)
9 | }
10 |
--------------------------------------------------------------------------------
/listings/src/main/kotlin/chapter05/Listing0520.kt:
--------------------------------------------------------------------------------
1 | package chapter05
2 |
3 | sealed class Component
4 | data class Composite(val children: List) : Component()
5 | data class Leaf(val value: Int): Component()
6 |
--------------------------------------------------------------------------------
/listings/src/main/kotlin/chapter05/Listing0521.kt:
--------------------------------------------------------------------------------
1 | package chapter05
2 |
3 | data class Person(val name: String = "", val alive: Boolean = true)
--------------------------------------------------------------------------------
/listings/src/main/kotlin/chapter05/Listing0522.kts:
--------------------------------------------------------------------------------
1 | import kotlin.reflect.KClass
2 | class A
3 | // ---------
4 |
5 | private val kclass: KClass = A::class
6 | private val jclass: Class = A::class.java
7 |
8 | // ---------
9 | println(kclass)
10 | println(jclass)
--------------------------------------------------------------------------------
/listings/src/main/kotlin/chapter05/Listing0523.kt:
--------------------------------------------------------------------------------
1 | class Customer
2 | class CreditCard
3 | // -----------------
4 |
5 | // Have the same JVM signature: validate(java.util.List)
6 | fun List.validate() {}
7 | //fun List.validate() {} // Clashing JVM signature
8 |
--------------------------------------------------------------------------------
/listings/src/main/kotlin/chapter05/Listing0524.kts:
--------------------------------------------------------------------------------
1 | // Must rename declarations to avoid name clashes with previous listing file in this project
2 |
3 | fun List.validate0524() { }
4 |
5 | @JvmName("validateCC") // Resolves the name clash
6 | fun List.validate0524() { }
7 |
8 | // Both can be called as validate() from Kotlin (because dispatched at compile-time)
9 | val customers = listOf(Customer())
10 | val ccs = listOf(CreditCard())
11 | customers.validate0524()
12 | ccs.validate0524()
13 |
--------------------------------------------------------------------------------
/listings/src/main/kotlin/chapter05/Listing0525.kt:
--------------------------------------------------------------------------------
1 | package chapter05
2 |
3 | inline fun require(predicate: Boolean, message: () -> String) {
4 | if (!predicate) println(message())
5 | }
6 |
7 | fun main() { // Listing uses main function to show decompiled code
8 | require(someCondition()) { "someCondition must be true" }
9 | }
10 |
11 | // -------
12 | fun someCondition() = false
13 |
--------------------------------------------------------------------------------
/listings/src/main/kotlin/chapter05/Listing0526.kt:
--------------------------------------------------------------------------------
1 | @file:JvmName("CsvUtils")
2 | package chapter05
3 |
4 | import java.io.File
5 | import java.io.FileNotFoundException
6 |
7 | @Throws(FileNotFoundException::class) // Generates throws-clause in bytecode
8 | fun readInput() = File("input.csv").readText()
9 |
10 |
--------------------------------------------------------------------------------
/listings/src/main/kotlin/chapter05/Listing0528.kt:
--------------------------------------------------------------------------------
1 | package chapter05
2 |
3 | class Stack(vararg items: E) { }
4 | fun consumeStack(stack: Stack) { }
5 |
--------------------------------------------------------------------------------
/listings/src/main/kotlin/chapter05/Listing0529.kt:
--------------------------------------------------------------------------------
1 | package chapter05
2 |
3 | // Declarations are renamed to avoid name clashes with previous listing file
4 |
5 | fun consumeStack0529(stack: Stack<@JvmSuppressWildcards Number>) { } // No more wildcards
6 |
7 | // Normally no wildcards are generated for return types, unless you use @JvmWildcard
8 | fun produceStack0529(): Stack<@JvmWildcard Number> {
9 | return Stack(4, 8, 15, 16, 23, 42)
10 | }
11 |
12 |
--------------------------------------------------------------------------------
/listings/src/main/kotlin/chapter05/Listing0530.kt:
--------------------------------------------------------------------------------
1 | @file:JvmName("Nothing")
2 |
3 | package chapter05
4 |
5 | fun fail(message: String): Nothing { // Indicates non-terminating function
6 | throw AssertionError(message)
7 | }
8 |
9 | fun takeNothing(perpetualMotion: Nothing) {} // Impossible to call from Kotlin
--------------------------------------------------------------------------------
/listings/src/main/kotlin/chapter06/Listing0601.kts:
--------------------------------------------------------------------------------
1 | class User
2 | class Location
3 | class Weather
4 |
5 | fun fetchUser(id: Int, callback: (User) -> Unit) {}
6 | fun fetchLocation(user: User, callback: (Location) -> Unit) {}
7 | fun fetchWeather(location: Location, callback: (Weather) -> Unit) {}
8 | fun updateUi(weather: Weather) {}
9 | val userId = 1
10 | // ------------
11 |
12 | fetchUser(userId) { user ->
13 | fetchLocation(user) { location ->
14 | fetchWeather(location) { weatherData ->
15 | updateUi(weatherData)
16 | }
17 | }
18 | }
19 |
--------------------------------------------------------------------------------
/listings/src/main/kotlin/chapter06/Listing0602.kts:
--------------------------------------------------------------------------------
1 | import java.util.concurrent.CompletableFuture
2 |
3 | class User
4 | class Location
5 | class Weather
6 |
7 | fun fetchUser(id: Int): CompletableFuture = CompletableFuture()
8 | fun fetchLocation(user: User): CompletableFuture = CompletableFuture()
9 | fun fetchWeather(location: Location): CompletableFuture = CompletableFuture()
10 | fun updateUi(weather: Weather) {}
11 | val userId = 1
12 | // ----------
13 |
14 | fetchUser(userId)
15 | .thenCompose { user -> fetchLocation(user) }
16 | .thenCompose { location -> fetchWeather(location) }
17 | .thenAccept { weatherData -> updateUi(weatherData) }
18 |
--------------------------------------------------------------------------------
/listings/src/main/kotlin/chapter06/Listing0604.kts:
--------------------------------------------------------------------------------
1 | import kotlinx.coroutines.GlobalScope
2 | import kotlinx.coroutines.launch
3 |
4 | class User
5 | class Location
6 | class Weather
7 | suspend fun fetchUser(id: Int): User = User()
8 | suspend fun fetchLocation(user: User): Location = Location()
9 | suspend fun fetchWeather(location: Location): Weather = Weather()
10 | fun updateUi(weather: Weather) {}
11 | val userId = 1
12 |
13 | GlobalScope.launch { // Avoid GlobalScope in production code
14 | // --------------
15 | val user = fetchUser(userId) // Asynchronous call in sequential style
16 | val location = fetchLocation(user)
17 | val weatherData = fetchWeather(location)
18 | updateUi(weatherData)
19 | }
20 |
--------------------------------------------------------------------------------
/listings/src/main/kotlin/chapter06/Listing0605.kts:
--------------------------------------------------------------------------------
1 | class User
2 | // ------------
3 |
4 | suspend fun fetchUser(): User { return User() }
--------------------------------------------------------------------------------
/listings/src/main/kotlin/chapter06/Listing0607.kts:
--------------------------------------------------------------------------------
1 | class User
2 | class Location
3 | class Weather
4 | suspend fun fetchUser(id: Int): User = User()
5 | suspend fun fetchLocation(user: User): Location = Location()
6 | suspend fun fetchWeather(location: Location): Weather = Weather()
7 | fun updateUi(weather: Weather) {}
8 | val userId = 1
9 | // --------------
10 |
11 | suspend fun updateWeather() {
12 | val user = fetchUser(userId) // Suspension point #1
13 | val location = fetchLocation(user) // Suspension point #2
14 | val weatherData = fetchWeather(location) // Suspension point #3
15 | updateUi(weatherData)
16 | }
17 |
--------------------------------------------------------------------------------
/listings/src/main/kotlin/chapter06/Listing0608.kts:
--------------------------------------------------------------------------------
1 | import java.util.concurrent.CompletableFuture
2 | import java.util.concurrent.Future
3 |
4 | class User
5 | // ----------
6 |
7 | fun fetchUserAsync(): Future { return CompletableFuture() }
8 |
9 | // Call-site
10 | val user = fetchUserAsync() // Asynchrony is explicit, so you expect a Future
--------------------------------------------------------------------------------
/listings/src/main/kotlin/chapter06/Listing0609.kts:
--------------------------------------------------------------------------------
1 | suspend fun updateWeather() {}
2 | // -----------
3 |
4 | fun wrapper() {
5 | // Error: Suspend function must be called from coroutine or other suspend function
6 | // updateWeather()
7 | }
--------------------------------------------------------------------------------
/listings/src/main/kotlin/chapter06/Listing0610.kts:
--------------------------------------------------------------------------------
1 | class User
2 | class Location
3 | class Weather
4 | suspend fun fetchUser(id: Int): User = User()
5 | suspend fun fetchLocations(user: User): List = listOf(Location(), Location())
6 | suspend fun fetchWeather(location: Location): Weather = Weather()
7 | fun updateUi(weather: Weather, location: Location) {}
8 | // --------------
9 |
10 | suspend fun updateWeather(userId: Int) {
11 | val user = fetchUser(userId)
12 | val locations = fetchLocations(user)
13 | for (location in locations) {
14 | val weatherData = fetchWeather(location)
15 | updateUi(weatherData, location)
16 | }
17 | }
18 |
--------------------------------------------------------------------------------
/listings/src/main/kotlin/chapter06/Listing0611.kts:
--------------------------------------------------------------------------------
1 | class User
2 | class Location
3 | class Weather
4 | suspend fun fetchUser(id: Int): User = User()
5 | suspend fun fetchLocation(user: User): Location = Location()
6 | suspend fun fetchWeather(location: Location): Weather = Weather()
7 | fun updateUi(weather: Weather) {}
8 | // --------------
9 |
10 | suspend fun updateWeather(userId: Int) {
11 | try {
12 | val user = fetchUser(userId)
13 | val location = fetchLocation(user)
14 | val weatherData = fetchWeather(location)
15 | updateUi(weatherData)
16 | } catch(e: Exception) {
17 | // Handle exception
18 | } finally {
19 | // Cleanup
20 | }
21 | }
22 |
--------------------------------------------------------------------------------
/listings/src/main/kotlin/chapter06/Listing0612.kt:
--------------------------------------------------------------------------------
1 | class User
2 | class Location
3 | class Weather
4 | suspend fun fetchUser(id: Int): User = User()
5 | suspend fun fetchLocations(user: User): List = listOf(Location(), Location())
6 | suspend fun fetchWeather(location: Location): Weather = Weather()
7 | fun updateUi(weather: Weather, location: Location) {}
8 | // --------------
9 |
10 | suspend fun updateWeather(userId: Int) {
11 | val user = fetchUser(userId)
12 | fetchLocations(user).forEach { // Can use all higher-order fcts. for collections
13 | val weatherData = fetchWeather(it) // Can be called inside higher-order function
14 | updateUi(weatherData, it)
15 | }
16 | }
--------------------------------------------------------------------------------
/listings/src/main/kotlin/chapter06/Listing0613.kts:
--------------------------------------------------------------------------------
1 | import kotlinx.coroutines.runBlocking
2 |
3 | runBlocking {
4 | // Can call suspending functions inside runBlocking
5 | updateWeather(42)
6 | }
7 |
--------------------------------------------------------------------------------
/listings/src/main/kotlin/chapter06/Listing0615.kts:
--------------------------------------------------------------------------------
1 | import kotlinx.coroutines.runBlocking
2 |
3 | annotation class Test
4 | // ------------
5 |
6 | fun main() = runBlocking { // Allows suspending calls in main
7 | updateWeather(42)
8 | }
9 |
10 | // In a test case…
11 | @Test fun testUpdateWeather() = runBlocking { updateWeather(42) }
12 |
--------------------------------------------------------------------------------
/listings/src/main/kotlin/chapter06/Listing0616.kts:
--------------------------------------------------------------------------------
1 | import kotlinx.coroutines.GlobalScope
2 | import kotlinx.coroutines.delay
3 | import kotlinx.coroutines.launch
4 |
5 | // Launches non-blocking coroutine (in global scope, do not use in production)
6 | GlobalScope.launch {
7 | println("Coroutine started") // 2nd print
8 | delay(1000) // Calls suspending function from within coroutine
9 | println("Coroutine finished") // 3rd print
10 | }
11 |
12 | println("main() continues") // 1st print
13 | Thread.sleep(1500) // Keep program alive
14 | println("main() finished") // 4th print
--------------------------------------------------------------------------------
/listings/src/main/kotlin/chapter06/Listing0617.kts:
--------------------------------------------------------------------------------
1 | import kotlinx.coroutines.delay
2 | import kotlinx.coroutines.launch
3 | import kotlinx.coroutines.runBlocking
4 |
5 | runBlocking { // runBlocking is required to call suspending ‘join’
6 |
7 | val job = launch { // launch returns a Job object
8 | println("Coroutine started")
9 | delay(1000)
10 | println("Coroutine finished")
11 | }
12 |
13 | println("main() continues")
14 | job.join() // Waits for completion of coroutine (non-blocking)
15 | println("main() finished")
16 | }
17 |
--------------------------------------------------------------------------------
/listings/src/main/kotlin/chapter06/Listing0618.kts:
--------------------------------------------------------------------------------
1 | import kotlinx.coroutines.delay
2 | import kotlinx.coroutines.launch
3 | import kotlinx.coroutines.runBlocking
4 |
5 | runBlocking {
6 |
7 | // Launch 100,000 coroutines
8 | val jobs = List(100_000) {
9 | launch {
10 | delay(1000)
11 | print("+")
12 | }
13 | }
14 | jobs.forEach { it.join() }
15 | }
16 |
--------------------------------------------------------------------------------
/listings/src/main/kotlin/chapter06/Listing0619.kts:
--------------------------------------------------------------------------------
1 | import kotlinx.coroutines.delay
2 | import kotlinx.coroutines.launch
3 | import kotlinx.coroutines.runBlocking
4 |
5 | runBlocking {
6 |
7 | val job = launch {
8 | repeat(10) {
9 | delay(300) // Cooperative delay from stdlib
10 | println("${it + 1} of 10...") // Only 1 of 10, 2 of 10, 3 of 10 are printed
11 | }
12 | }
13 |
14 | delay(1000)
15 | println("runBlocking: No more time")
16 | job.cancel() // Can control cancellation on per-coroutine level
17 | job.join() // Then wait for it to cancel
18 | println("runBlocking: Now ready to quit")
19 | }
20 |
--------------------------------------------------------------------------------
/listings/src/main/kotlin/chapter06/Listing0620.kts:
--------------------------------------------------------------------------------
1 | import kotlinx.coroutines.cancelAndJoin
2 | import kotlinx.coroutines.delay
3 | import kotlinx.coroutines.launch
4 | import kotlinx.coroutines.runBlocking
5 |
6 | runBlocking {
7 | val job = launch {
8 | repeat(10) {
9 | Thread.sleep(300) // Non-cooperative
10 | println("${it + 1} of 10...") // All ten iterations are executed
11 | }
12 | }
13 |
14 | delay(1000)
15 | job.cancelAndJoin() // Cancel is ignored, so this will wait for another two seconds
16 | }
17 |
--------------------------------------------------------------------------------
/listings/src/main/kotlin/chapter06/Listing0621.kts:
--------------------------------------------------------------------------------
1 | import kotlinx.coroutines.*
2 |
3 | runBlocking {
4 | val job = launch {
5 | repeat(10) {
6 | if (isActive) { // Cooperative
7 | Thread.sleep(300)
8 | println("${it + 1} of 10...")
9 | }
10 | }
11 | }
12 |
13 | delay(1000)
14 | job.cancelAndJoin()
15 | }
16 |
--------------------------------------------------------------------------------
/listings/src/main/kotlin/chapter06/Listing0622.kts:
--------------------------------------------------------------------------------
1 | import kotlinx.coroutines.GlobalScope
2 | import kotlinx.coroutines.async
3 | import kotlinx.coroutines.delay
4 | import kotlinx.coroutines.runBlocking
5 |
6 | fun fetchFirstAsync() = GlobalScope.async { // Return type is Deferred
7 | delay(1000)
8 | 294 // Return value of lambda, type Int
9 | }
10 |
11 | fun fetchSecondAsync() = GlobalScope.async { // Return type is Deferred
12 | delay(1000)
13 | 7 // Return value of lambda, type Int
14 | }
15 |
16 | runBlocking {
17 |
18 | // Asynchronous composition: total runtime ~1s
19 | val first = fetchFirstAsync() // Inferred type: Deferred
20 | val second = fetchSecondAsync() // Inferred type: Deferred
21 | val result = first.await() / second.await() // Awaits completion; now type is Int
22 | println("Result: $result") // 42
23 | }
24 |
--------------------------------------------------------------------------------
/listings/src/main/kotlin/chapter06/Listing0623.kts:
--------------------------------------------------------------------------------
1 | import kotlinx.coroutines.GlobalScope
2 | import kotlinx.coroutines.async
3 | import kotlinx.coroutines.delay
4 | import kotlinx.coroutines.runBlocking
5 |
6 | fun fetchFirstAsync() = GlobalScope.async { // Return type is Deferred
7 | delay(1000)
8 | 294 // Return value of lambda, type Int
9 | }
10 |
11 | fun fetchSecondAsync() = GlobalScope.async { // Return type is Deferred
12 | delay(1000)
13 | 7 // Return value of lambda, type Int
14 | }
15 | // ------------------
16 |
17 | runBlocking {
18 |
19 | // Effectively synchronous: total runtime ~2s
20 | val first = fetchFirstAsync().await() // Inferred type: Int (runtime ~1s)
21 | val second = fetchSecondAsync().await() // Inferred type: Int (runtime ~1s)
22 | }
23 |
--------------------------------------------------------------------------------
/listings/src/main/kotlin/chapter06/Listing0624.kts:
--------------------------------------------------------------------------------
1 | import kotlinx.coroutines.async
2 | import kotlinx.coroutines.delay
3 | import kotlinx.coroutines.runBlocking
4 |
5 | suspend fun fetchFirst(): Int {
6 | delay(1000)
7 | return 294
8 | }
9 |
10 | suspend fun fetchSecond(): Int {
11 | delay(1000)
12 | return 7
13 | }
14 |
15 | runBlocking {
16 | val a = async { fetchFirst() } // Asynchrony is explicit
17 | val b = async { fetchSecond() } // Asynchrony is explicit
18 |
19 | println("Result: ${a.await() / b.await()}")
20 | }
21 |
--------------------------------------------------------------------------------
/listings/src/main/kotlin/chapter06/Listing0625.kts:
--------------------------------------------------------------------------------
1 | import kotlinx.coroutines.Deferred
2 | import kotlinx.coroutines.async
3 | import kotlinx.coroutines.runBlocking
4 |
5 | runBlocking {
6 | val deferred: Deferred = async { throw Exception("Failed...") }
7 |
8 | try {
9 | deferred.await() // Tries to get result of asynchronous call, throws exception
10 | } catch (e: Exception) {
11 | // Handle failure case here
12 | println("Exception was thrown")
13 | }
14 | }
15 |
--------------------------------------------------------------------------------
/listings/src/main/kotlin/chapter06/Listing0626.kts:
--------------------------------------------------------------------------------
1 | class Weather
2 | fun updateUi(w: Weather) {}
3 | val weatherData = Weather()
4 | // ---------------
5 |
6 | // Android-specific example (requires Android context)
7 | //CoroutineScope(Dispatchers.Main).launch { // Main dispatcher refers to UI context on Android
8 | // updateUi(weatherData)
9 | //}
10 |
--------------------------------------------------------------------------------
/listings/src/main/kotlin/chapter06/Listing0627.kts:
--------------------------------------------------------------------------------
1 | // Android-specific example (requires Android context)
2 | //val UI = HandlerContext(Handler(Looper.getMainLooper()), "UI")
--------------------------------------------------------------------------------
/listings/src/main/kotlin/chapter06/Listing0628.kts:
--------------------------------------------------------------------------------
1 | import kotlinx.coroutines.CoroutineScope
2 | import kotlinx.coroutines.Dispatchers
3 | import kotlinx.coroutines.GlobalScope
4 | import kotlinx.coroutines.launch
5 |
6 | GlobalScope.launch { println("Running on ${Thread.currentThread().name}") }
7 | CoroutineScope(Dispatchers.IO).launch { println("Running on ${Thread.currentThread().name}") }
8 | CoroutineScope(Dispatchers.Default).launch { println("Running on ${Thread.currentThread().name}") }
9 |
--------------------------------------------------------------------------------
/listings/src/main/kotlin/chapter06/Listing0629.kts:
--------------------------------------------------------------------------------
1 | import kotlinx.coroutines.CoroutineScope
2 | import kotlinx.coroutines.launch
3 | import kotlinx.coroutines.newSingleThreadContext
4 |
5 | CoroutineScope(newSingleThreadContext("My New Thread")).launch { // Runs coroutine in new thread
6 | println("Running on ${Thread.currentThread().name}")
7 | }
--------------------------------------------------------------------------------
/listings/src/main/kotlin/chapter06/Listing0630.kts:
--------------------------------------------------------------------------------
1 | import kotlinx.coroutines.CoroutineScope
2 | import kotlinx.coroutines.Dispatchers
3 | import kotlinx.coroutines.launch
4 |
5 | CoroutineScope(Dispatchers.Unconfined).launch {
6 | println("Running on ${Thread.currentThread().name}")
7 | }
--------------------------------------------------------------------------------
/listings/src/main/kotlin/chapter06/Listing0631.kts:
--------------------------------------------------------------------------------
1 | import kotlinx.coroutines.GlobalScope
2 | import kotlinx.coroutines.launch
3 |
4 | GlobalScope.launch {
5 | launch(coroutineContext) { // Launches child coroutine
6 | println("Running on ${Thread.currentThread().name}")
7 | }
8 | }
--------------------------------------------------------------------------------
/listings/src/main/kotlin/chapter06/Listing0632.kts:
--------------------------------------------------------------------------------
1 | import kotlinx.coroutines.*
2 |
3 | runBlocking {
4 | val jobs = mutableListOf()
5 |
6 | // DefaultDispatcher (CommonPool at the time of writing)
7 | jobs += launch {
8 | println("Default: In thread ${Thread.currentThread().name} before delay")
9 | delay(500)
10 | println("Default: In thread ${Thread.currentThread().name} after delay")
11 | }
12 |
13 | jobs += launch(newSingleThreadContext("New Thread")) {
14 | println("New Thread: In thread ${Thread.currentThread().name} before delay")
15 | delay(500)
16 | println("New Thread: In thread ${Thread.currentThread().name} after delay")
17 | }
18 |
19 | jobs += launch(Dispatchers.Unconfined) {
20 | println("Unconfined: In thread ${Thread.currentThread().name} before delay")
21 | delay(500)
22 | println("Unconfined: In thread ${Thread.currentThread().name} after delay")
23 | }
24 |
25 | jobs += launch(coroutineContext) {
26 | println("cC: In thread ${Thread.currentThread().name} before delay")
27 | delay(500)
28 | println("cC: In thread ${Thread.currentThread().name} after delay")
29 | }
30 |
31 | jobs.forEach { it.join() }
32 | }
33 |
--------------------------------------------------------------------------------
/listings/src/main/kotlin/chapter06/Listing0633.kts:
--------------------------------------------------------------------------------
1 | class User
2 | class Location
3 | class Weather
4 | suspend fun fetchUser(id: Int): User = User()
5 | suspend fun fetchLocation(user: User): Location = Location()
6 | suspend fun fetchWeather(location: Location): Weather = Weather()
7 | fun updateUi(weather: Weather) {}
8 | // --------------
9 |
10 | // Android-specific example (requires Android context)
11 | //suspend fun updateWeather(userId: Int) {
12 | // val user = fetchUser(userId)
13 | // val location = fetchLocation(user)
14 | // val weatherData = fetchWeather(location)
15 | //
16 | // withContext(Dispatchers.Main) { // Refers to UI context on Android
17 | // updateUi(weatherData)
18 | // }
19 | //}
20 | //
21 | //// Call-site
22 | //runBlocking {
23 | // launch { updateWeather(42) }
24 | //}
25 |
--------------------------------------------------------------------------------
/listings/src/main/kotlin/chapter06/Listing0634.kts:
--------------------------------------------------------------------------------
1 | import kotlinx.coroutines.delay
2 | import kotlinx.coroutines.runBlocking
3 | import kotlinx.coroutines.withTimeout
4 |
5 | runBlocking {
6 | withTimeout(1200) {
7 | repeat(10) {
8 | delay(500)
9 | println("${it + 1} of 10")
10 | }
11 | }
12 | }
13 |
--------------------------------------------------------------------------------
/listings/src/main/kotlin/chapter06/Listing0635.kts:
--------------------------------------------------------------------------------
1 | import kotlinx.coroutines.TimeoutCancellationException
2 | import kotlinx.coroutines.delay
3 | import kotlinx.coroutines.runBlocking
4 | import kotlinx.coroutines.withTimeout
5 |
6 | runBlocking {
7 | try {
8 | withTimeout(1200) {
9 | repeat(10) {
10 | delay(500)
11 | println("${it + 1} of 10")
12 | }
13 | }
14 | } catch (e: TimeoutCancellationException) {
15 | println("Time is up!")
16 | } finally {
17 | println("Cleaning up open connections...")
18 | }
19 | }
--------------------------------------------------------------------------------
/listings/src/main/kotlin/chapter06/Listing0636.kts:
--------------------------------------------------------------------------------
1 | import kotlinx.coroutines.cancelAndJoin
2 | import kotlinx.coroutines.delay
3 | import kotlinx.coroutines.launch
4 | import kotlinx.coroutines.runBlocking
5 |
6 | runBlocking {
7 | val parent = launch {
8 | repeat(10) { i ->
9 | launch { // Implicitly uses coroutineContext, thus parent context
10 | delay(300L * (i + 1))
11 | println("Coroutine ${i + 1} finished") // Only 1, 2, and 3 get to print
12 | }
13 | }
14 | }
15 |
16 | delay(1000)
17 | parent.cancelAndJoin() // Cancels parent coroutine along with all its children
18 | }
19 |
--------------------------------------------------------------------------------
/listings/src/main/kotlin/chapter06/Listing0637.kt:
--------------------------------------------------------------------------------
1 | import kotlinx.coroutines.Job
2 |
3 | interface JobHolder { val job: Job }
--------------------------------------------------------------------------------
/listings/src/main/kotlin/chapter06/Listing0638.kt:
--------------------------------------------------------------------------------
1 | // Android-specific example (requires Android context)
2 | //class MainActivity : AppCompatActivity(), JobHolder {
3 | // override val job = Job()
4 | // override fun onDestroy() {
5 | // super.onDestroy()
6 | // job.cancel()
7 | // }
8 | //}
9 |
--------------------------------------------------------------------------------
/listings/src/main/kotlin/chapter06/Listing0639.kt:
--------------------------------------------------------------------------------
1 | // Android-specific example
2 |
3 | //import android.view.View
4 | //import kotlinx.coroutines.experimental.NonCancellable
5 | //
6 | //val View.contextJob
7 | // get() = (context as? JobHolder)?.job ?: NonCancellable
8 |
--------------------------------------------------------------------------------
/listings/src/main/kotlin/chapter06/Listing0640.kts:
--------------------------------------------------------------------------------
1 | import kotlinx.coroutines.CoroutineExceptionHandler
2 | import kotlinx.coroutines.CoroutineName
3 | import kotlinx.coroutines.CoroutineScope
4 | import kotlinx.coroutines.launch
5 |
6 | // CoroutineName
7 | val name = CoroutineName("Koroutine")
8 |
9 | CoroutineScope(name).launch { }
10 |
11 | // CoroutineExceptionHandler
12 | val exceptionHandler = CoroutineExceptionHandler { context, exception ->
13 | println("Crashed with context $context")
14 | exception.printStackTrace()
15 | }
16 |
17 | CoroutineScope(exceptionHandler).launch { }
18 |
--------------------------------------------------------------------------------
/listings/src/main/kotlin/chapter06/Listing0642.kts:
--------------------------------------------------------------------------------
1 | import kotlinx.coroutines.*
2 |
3 | val name = CoroutineName("Koroutine")
4 | val exceptionHandler = CoroutineExceptionHandler { context, exception ->
5 | println("Crashed with context $context")
6 | exception.printStackTrace()
7 | }
8 | val contextJob = NonCancellable
9 | // -----------------
10 |
11 | // CoroutineName + CoroutineExceptionHandler
12 | CoroutineScope(name + exceptionHandler).launch { }
13 |
14 | // Job + CoroutineDispatcher
15 | CoroutineScope(contextJob + Dispatchers.Main).launch { }
16 |
--------------------------------------------------------------------------------
/listings/src/main/kotlin/chapter06/Listing0643.kts:
--------------------------------------------------------------------------------
1 | import kotlinx.coroutines.*
2 | import kotlin.coroutines.ContinuationInterceptor
3 |
4 | val name = CoroutineName("Koroutine")
5 | val exceptionHandler = CoroutineExceptionHandler { context, exception ->
6 | println("Crashed with context $context")
7 | exception.printStackTrace()
8 | }
9 | // ------------
10 | CoroutineScope(name + exceptionHandler).launch {
11 | println("Context: ${coroutineContext}")
12 | println("Job: ${coroutineContext[Job]}")
13 | println("Dispatcher: ${coroutineContext[ContinuationInterceptor]}")
14 | println("Name: ${coroutineContext[CoroutineName]}")
15 | println("Exception Handler: ${coroutineContext[CoroutineExceptionHandler]}")
16 | }
17 |
--------------------------------------------------------------------------------
/listings/src/main/kotlin/chapter06/Listing0644.kts:
--------------------------------------------------------------------------------
1 | import kotlinx.coroutines.CoroutineStart
2 | import kotlinx.coroutines.delay
3 | import kotlinx.coroutines.launch
4 | import kotlinx.coroutines.runBlocking
5 |
6 | runBlocking {
7 | val job = launch(start = CoroutineStart.LAZY) { // Runs only when triggered
8 | println("Lazy coroutine started")
9 | }
10 |
11 | println("Moving on...")
12 | delay(1000)
13 | println("Still no coroutine started")
14 | job.join() // Triggers execution of the coroutine using join()
15 | println("Joined coroutine")
16 | }
17 |
--------------------------------------------------------------------------------
/listings/src/main/kotlin/chapter06/Listing0645.kts:
--------------------------------------------------------------------------------
1 | import kotlinx.coroutines.coroutineScope
2 | import kotlinx.coroutines.delay
3 | import kotlinx.coroutines.future.await
4 | import kotlinx.coroutines.future.future
5 | import kotlinx.coroutines.runBlocking
6 |
7 | suspend fun fetchValueAsync() = coroutineScope {
8 | future {
9 | delay(500)
10 | 7
11 | }
12 | }
13 |
14 | runBlocking {
15 | fetchValueAsync().thenApply { it * 6 }
16 | .thenAccept { println("Retrieved value: $it") }
17 | .await()
18 | }
19 |
--------------------------------------------------------------------------------
/listings/src/main/kotlin/chapter06/Listing0646.kts:
--------------------------------------------------------------------------------
1 | val fibonacci = sequence {
2 | yield(1)
3 | var a = 0
4 | var b = 1
5 | while (true) {
6 | val next = a + b
7 | yield(next)
8 | a = b
9 | b = next
10 | }
11 | }
12 |
13 | fibonacci.take(10).forEach(::println) // 1, 1, 2, 3, 5, 8, …
14 |
--------------------------------------------------------------------------------
/listings/src/main/kotlin/chapter06/Listing0649.kts:
--------------------------------------------------------------------------------
1 | import kotlinx.coroutines.channels.actor
2 | import kotlinx.coroutines.runBlocking
3 |
4 | runBlocking {
5 |
6 | val actor = actor {
7 | val message = channel.receive()
8 | println(message)
9 | }
10 |
11 | actor.send("Hello World!") // Sends an element to the actor’s channel
12 | actor.close() // Closes channel because actor is no longer needed
13 | }
14 |
--------------------------------------------------------------------------------
/listings/src/main/kotlin/chapter06/Listing0650.kts:
--------------------------------------------------------------------------------
1 | import kotlinx.coroutines.channels.actor
2 | import kotlinx.coroutines.runBlocking
3 |
4 |
5 | runBlocking {
6 |
7 | // This actor keeps reading from its channel indefinitely
8 | val actor = actor {
9 | for (value in channel) {
10 | println(value)
11 | }
12 | }
13 |
14 | actor.send("Hello") // Makes actor print out Hello
15 | actor.send("World") // Makes actor print out World
16 | actor.close()
17 | }
18 |
--------------------------------------------------------------------------------
/listings/src/main/kotlin/chapter06/Listing0651.kts:
--------------------------------------------------------------------------------
1 | import kotlinx.coroutines.channels.Channel
2 | import kotlinx.coroutines.channels.actor
3 | import kotlinx.coroutines.delay
4 | import kotlinx.coroutines.runBlocking
5 |
6 | runBlocking {
7 | val actor = actor(capacity = Channel.CONFLATED) { // Conflated channel
8 | for (value in channel) { println(value) }
9 | }
10 |
11 | actor.send("Hello") // Will be overwritten by following element
12 | actor.send("World") // Overwrites Hello if it was not yet consumed by the actor
13 | delay(500)
14 | actor.close()
15 | }
16 |
--------------------------------------------------------------------------------
/listings/src/main/kotlin/chapter06/Listing0652.kts:
--------------------------------------------------------------------------------
1 | import kotlinx.coroutines.channels.Channel
2 | import kotlinx.coroutines.launch
3 | import kotlinx.coroutines.runBlocking
4 | import kotlinx.coroutines.selects.select
5 |
6 | runBlocking {
7 | val channel1 = Channel()
8 | val channel2 = Channel()
9 |
10 | launch { // No need for the actor coroutine builder
11 | while (true) {
12 | select { // Provide any number of alternative data sources in here
13 | channel1.onReceive { println("From channel 1: $it") }
14 | channel2.onReceive { println("From channel 2: $it") }
15 | }
16 | }
17 | }
18 |
19 | channel1.send(17) // Sends 17 to channel 1, thus this source is selected first
20 | channel2.send(42) // Sends 42 to channel 2, causing channel 2 to be selected next
21 | channel1.close(); channel2.close()
22 | }
23 |
--------------------------------------------------------------------------------
/listings/src/main/kotlin/chapter06/Listing0653.kts:
--------------------------------------------------------------------------------
1 | import kotlinx.coroutines.channels.Channel
2 | import kotlinx.coroutines.channels.consumeEach
3 | import kotlinx.coroutines.channels.take
4 | import kotlinx.coroutines.launch
5 | import kotlinx.coroutines.runBlocking
6 |
7 | runBlocking {
8 | val channel = Channel()
9 | repeat(3) { n ->
10 | launch {
11 | while (true) {
12 | channel.send("Message from actor $n")
13 | }
14 | }
15 | }
16 |
17 | channel.take(10).consumeEach { println(it) }
18 | channel.close()
19 | }
20 |
--------------------------------------------------------------------------------
/listings/src/main/kotlin/chapter06/Listing0654.kts:
--------------------------------------------------------------------------------
1 | import kotlinx.coroutines.channels.consumeEach
2 | import kotlinx.coroutines.channels.produce
3 | import kotlinx.coroutines.channels.take
4 | import kotlinx.coroutines.runBlocking
5 |
6 | runBlocking {
7 | val producer = produce {
8 | var next = 1
9 |
10 | while (true) {
11 | send(next)
12 | next *= 2
13 | }
14 | }
15 |
16 | producer.take(10).consumeEach { println(it) }
17 | }
18 |
--------------------------------------------------------------------------------
/listings/src/main/kotlin/chapter06/Listing0655.kts:
--------------------------------------------------------------------------------
1 | import chapter06.Deposit
2 | import chapter06.Transaction
3 | import chapter06.Withdrawal
4 | import kotlinx.coroutines.GlobalScope
5 | import kotlinx.coroutines.channels.actor
6 | import kotlinx.coroutines.runBlocking
7 |
8 | fun newAccount(startBalance: Int) = GlobalScope.actor(capacity = 10) {
9 | var balance = startBalance
10 | for (tx in channel) {
11 | when (tx) {
12 | is Deposit -> { balance += tx.amount; println("New balance: $balance") }
13 | is Withdrawal -> { balance -= tx.amount; println("New balance: $balance") }
14 | }
15 | }
16 | }
17 |
18 | runBlocking {
19 | val bankAccount = newAccount(1000)
20 | bankAccount.send(Deposit(500))
21 | bankAccount.send(Withdrawal(1700))
22 | bankAccount.send(Deposit(4400))
23 | }
24 |
--------------------------------------------------------------------------------
/listings/src/main/kotlin/chapter06/Listing0655_Classes.kt:
--------------------------------------------------------------------------------
1 | package chapter06
2 |
3 | sealed class Transaction
4 | data class Deposit(val amount: Int) : Transaction()
5 | data class Withdrawal(val amount: Int) : Transaction()
--------------------------------------------------------------------------------
/listings/src/main/kotlin/chapter06/Listing0656.kts:
--------------------------------------------------------------------------------
1 | import javafx.scene.image.Image
2 | import java.util.concurrent.CompletableFuture
3 |
4 | // Callbacks
5 | fun fetchImageAsync(callback: (Image) -> Unit) { }
6 |
7 | // Futures
8 | fun fetchImageAsync(): CompletableFuture { return CompletableFuture() }
9 |
10 | // Coroutines
11 | suspend fun fetchImage(): Image { return Image("") }
12 |
--------------------------------------------------------------------------------
/listings/src/main/kotlin/chapter06/Listing0657.kts:
--------------------------------------------------------------------------------
1 | // Use compiler flag: -Dkotlinx.coroutines.debug
2 | fun log(message: String) = println("[${Thread.currentThread().name}] $message")
--------------------------------------------------------------------------------
/listings/src/main/kotlin/chapter06/Listing0658.kts:
--------------------------------------------------------------------------------
1 | // Android-specific example
2 | //class App : Application() {
3 | //
4 | // override fun onCreate() {
5 | // super.onCreate()
6 | // System.setProperty("kotlinx.coroutines.debug",
7 | // if (BuildConfig.DEBUG) "on" else "off")
8 | // }
9 | //}
10 |
11 | // In AndroidManifest.xml (XML)
12 | //
13 | //
14 |
--------------------------------------------------------------------------------
/listings/src/main/kotlin/chapter06/Listing0660.kts:
--------------------------------------------------------------------------------
1 | import kotlinx.coroutines.delay
2 | import kotlinx.coroutines.future.future
3 | import kotlinx.coroutines.runBlocking
4 | import kotlinx.coroutines.suspendCancellableCoroutine
5 | import java.util.concurrent.CompletableFuture
6 | import kotlin.coroutines.resume
7 | import kotlin.coroutines.resumeWithException
8 |
9 | suspend fun CompletableFuture.myAwait(): T {
10 | return suspendCancellableCoroutine { continuation ->
11 | whenComplete { value, exception ->
12 | if (exception == null) continuation.resume(value)
13 | else continuation.resumeWithException(exception)
14 | }
15 | }
16 | }
17 |
18 | runBlocking {
19 | val completable = future { delay(1000); 42 }
20 | println(completable.myAwait()) // 42
21 |
22 | val fail = future { throw Exception("Could not fetch value") }
23 | println(fail.myAwait()) // Throws exception
24 | }
25 |
--------------------------------------------------------------------------------
/listings/src/main/kotlin/chapter06/Listing0661.kt:
--------------------------------------------------------------------------------
1 | package chapter06
2 |
3 | import kotlinx.coroutines.GlobalScope
4 | import kotlinx.coroutines.future.future
5 |
6 | suspend fun fetchScore(): Int { return 42 } // This can hardly be called directly from Java
7 | fun fetchScoreAsync() = GlobalScope.future { fetchScore() } // This is to be used from Java
8 |
--------------------------------------------------------------------------------
/listings/src/main/kotlin/chapter06/Listing0662.kts:
--------------------------------------------------------------------------------
1 | import kotlinx.coroutines.delay
2 |
3 | suspend fun otherSuspend() {}
4 | // ----------
5 |
6 | suspend fun suspending(): Int {
7 | delay(1000) // Suspension point #1
8 | val a = 17
9 | otherSuspend() // Suspension point #2
10 | println(a)
11 |
12 | return 0
13 | }
14 |
--------------------------------------------------------------------------------
/listings/src/main/kotlin/chapter09/Listing0901.kts:
--------------------------------------------------------------------------------
1 | import java.time.LocalDate
2 | import java.time.Month
3 |
4 | fun user(init: User.() -> Unit) = User().apply(init)
5 | infix fun Int.January(year: Int) = LocalDate.of(year, Month.JANUARY, this)
6 |
7 | data class User(
8 | var username: String = "",
9 | var birthday: LocalDate? = null,
10 | var address: Address? = null
11 | ) {
12 | fun address(init: Address.() -> Unit) {
13 | address = Address().apply(init)
14 | }
15 | }
16 | data class Address(var street: String = "", var number: Int = -1, var postCode: String = "", var city: String = ""
17 | )
18 | // --------------
19 |
20 | user {
21 | username = "johndoe"
22 | birthday = 1 January 1984
23 | address {
24 | street = "Main Street"
25 | number = 42
26 | postCode = "12345"
27 | city = "New York"
28 | }
29 | }
30 |
--------------------------------------------------------------------------------
/listings/src/main/kotlin/chapter09/Listing0902.kts:
--------------------------------------------------------------------------------
1 | class User
2 | // ------------
3 |
4 | fun user(init: User.() -> Unit): User {
5 | val user = User()
6 | user.init()
7 | return user
8 | }
9 |
--------------------------------------------------------------------------------
/listings/src/main/kotlin/chapter09/Listing0903.kts:
--------------------------------------------------------------------------------
1 | class User
2 | // ------------
3 |
4 | fun user(init: User.() -> Unit) = User().apply(init)
--------------------------------------------------------------------------------
/listings/src/main/kotlin/chapter09/Listing0904.kts:
--------------------------------------------------------------------------------
1 | import java.time.LocalDate
2 |
3 | class Address
4 | // ------------
5 |
6 | data class User(
7 | var username: String = "",
8 | var birthday: LocalDate? = null,
9 | var address: Address? = null
10 | )
11 |
--------------------------------------------------------------------------------
/listings/src/main/kotlin/chapter09/Listing0905.kts:
--------------------------------------------------------------------------------
1 | import java.time.LocalDate
2 |
3 | data class User(
4 | var username: String = "",
5 | var birthday: LocalDate? = null,
6 | var address: Address? = null
7 | ) {
8 | fun address(init: Address.() -> Unit) {
9 | address = Address().apply(init)
10 | }
11 | }
12 |
13 | data class Address(
14 | var street: String = "",
15 | var number: Int = -1,
16 | var postCode: String = "",
17 | var city: String = ""
18 | )
19 |
--------------------------------------------------------------------------------
/listings/src/main/kotlin/chapter09/Listing0906.kts:
--------------------------------------------------------------------------------
1 | import java.time.LocalDate
2 | import java.time.Month
3 |
4 | fun user(init: User.() -> Unit) = User().apply(init)
5 |
6 | data class User(
7 | var username: String = "",
8 | var birthday: LocalDate? = null,
9 | var address: Address? = null
10 | ) {
11 | fun address(init: Address.() -> Unit) {
12 | address = Address().apply(init)
13 | }
14 | }
15 | data class Address(var street: String = "", var number: Int = -1, var postCode: String = "", var city: String = ""
16 | )
17 | // --------------
18 |
19 | user {
20 | address {
21 | username = "this-should-not-work"
22 | user {
23 | address {
24 | birthday = LocalDate.of(1984, Month.JANUARY, 1)
25 | }
26 | }
27 | }
28 | }
29 |
--------------------------------------------------------------------------------
/listings/src/main/kotlin/chapter09/Listing0907.kts:
--------------------------------------------------------------------------------
1 | import java.time.LocalDate
2 |
3 | data class User(val username: String, val birthday: LocalDate, val address: Address)
4 |
5 | data class Address(
6 | val street: String,
7 | val number: Int,
8 | val postCode: String,
9 | val city: String
10 | )
11 |
--------------------------------------------------------------------------------
/listings/src/main/kotlin/chapter09/Listing0908.kts:
--------------------------------------------------------------------------------
1 | import java.time.LocalDate
2 |
3 | data class User(val username: String, val birthday: LocalDate, val address: Address)
4 | data class Address(
5 | val street: String,
6 | val number: Int,
7 | val postCode: String,
8 | val city: String
9 | )
10 | class AddressBuilder { fun build(): Address = Address("", 1, "", "") }
11 | // ---------------
12 |
13 | class UserBuilder {
14 | var username = "" // Gets assigned directly in DSL => public
15 | var birthday: LocalDate? = null // Gets assigned directly in DSL => public
16 | private var address: Address? = null // Is built via builder => private
17 |
18 | fun address(init: AddressBuilder.() -> Unit) { // Nested function to build address
19 | address = AddressBuilder().apply(init).build()
20 | }
21 |
22 | fun build(): User { // Validates data and builds user object
23 | val theBirthday = birthday
24 | val theAddress = address
25 | if (username.isBlank() || theBirthday == null || theAddress == null)
26 | throw IllegalStateException("Please set username, birthday, and address.")
27 |
28 | return User(username, theBirthday, theAddress)
29 | }
30 | }
31 |
--------------------------------------------------------------------------------
/listings/src/main/kotlin/chapter09/Listing0909.kts:
--------------------------------------------------------------------------------
1 | data class Address(
2 | val street: String,
3 | val number: Int,
4 | val postCode: String,
5 | val city: String
6 | )
7 | // -----------------
8 |
9 | class AddressBuilder {
10 | var street = ""
11 | var number = -1
12 | var postCode = ""
13 | var city = ""
14 |
15 | fun build(): Address {
16 | if (notReady())
17 | throw IllegalStateException("Please set street, number, postCode, and city.")
18 |
19 | return Address(street, number, postCode, city)
20 | }
21 |
22 | private fun notReady()
23 | = arrayOf(street, postCode, city).any { it.isBlank() } || number <= 0
24 | }
25 |
--------------------------------------------------------------------------------
/listings/src/main/kotlin/chapter09/Listing0910.kts:
--------------------------------------------------------------------------------
1 | import java.time.LocalDate
2 |
3 | data class User(val username: String, val birthday: LocalDate, val address: Address)
4 | data class Address(val street: String, val number: Int, val postCode: String, val city: String)
5 | class AddressBuilder { fun build(): Address = Address("", 1, "", "") }
6 | class UserBuilder {
7 | var username = "" // Gets assigned directly in DSL => public
8 | var birthday: LocalDate? = null // Gets assigned directly in DSL => public
9 | private var address: Address? = null // Is built via builder => private
10 |
11 | fun address(init: AddressBuilder.() -> Unit) { // Nested function to build address
12 | address = AddressBuilder().apply(init).build()
13 | }
14 |
15 | fun build(): User { // Validates data and builds user object
16 | val theBirthday = birthday
17 | val theAddress = address
18 | if (username.isBlank() || theBirthday == null || theAddress == null)
19 | throw IllegalStateException("Please set username, birthday, and address.")
20 |
21 | return User(username, theBirthday, theAddress)
22 | }
23 | }
24 | // ----------------
25 |
26 | fun user(init: UserBuilder.() -> Unit) = UserBuilder().apply(init).build()
--------------------------------------------------------------------------------
/listings/src/main/kotlin/chapter09/Listing0911.kts:
--------------------------------------------------------------------------------
1 | import java.time.LocalDate
2 |
3 | data class Address(val street: String, val number: Int, val postCode: String, val city: String)
4 | class AddressBuilder { fun build(): Address = Address("", 1, "", "") }
5 | // ------------
6 |
7 | data class User(val username: String, val birthday: LocalDate, val addresses: List)
8 |
9 | class UserBuilder {
10 | var username = "" // Gets assigned directly in DSL => public
11 | var birthday: LocalDate? = null // Gets assigned directly in DSL => public
12 | private val addresses: MutableList = mutableListOf()
13 |
14 | fun address(init: AddressBuilder.() -> Unit) {
15 | addresses.add(AddressBuilder().apply(init).build())
16 | }
17 |
18 | fun build(): User { // Validates data and builds user object
19 | val theBirthday = birthday
20 | if (username.isBlank() || theBirthday == null || addresses.isNotEmpty())
21 | throw IllegalStateException("Please set username, birthday, and address.")
22 |
23 | return User(username, theBirthday, addresses)
24 | }
25 | }
--------------------------------------------------------------------------------
/listings/src/main/kotlin/chapter09/Listing0913.kts:
--------------------------------------------------------------------------------
1 | import java.time.LocalDate
2 |
3 | data class Address(val street: String, val number: Int, val postCode: String, val city: String)
4 | class AddressBuilder {
5 | var street = ""
6 | var number = -1
7 | var postCode = ""
8 | var city = ""
9 |
10 | fun build(): Address {
11 | if (notReady())
12 | throw IllegalStateException("Please set street, number, postCode, and city.")
13 |
14 | return Address(street, number, postCode, city)
15 | }
16 |
17 | private fun notReady()
18 | = arrayOf(street, postCode, city).any { it.isBlank() } || number <= 0
19 | }
20 | data class User(val username: String, val birthday: LocalDate, val addresses: List)
21 | // -------------
22 |
23 | class UserBuilder {
24 | var username = "" // Gets assigned directly in DSL => public
25 | var birthday: LocalDate? = null // Gets assigned directly in DSL => public
26 | private val addresses: MutableList = mutableListOf()
27 |
28 | inner class Addresses : ArrayList() {
29 | fun address(init: AddressBuilder.() -> Unit) {
30 | add(AddressBuilder().apply(init).build())
31 | }
32 | }
33 |
34 | fun addresses(init: Addresses.() -> Unit) { // ‘Addresses’ is the receiver now
35 | addresses.addAll(Addresses().apply(init))
36 | }
37 |
38 | fun build(): User {
39 | val theBirthday = birthday
40 | if (username.isBlank() || theBirthday == null || addresses.isEmpty()) throw IllegalStateException("Please define a username, birthday, and at least one address.")
41 |
42 | return User(username, theBirthday, addresses)
43 | }
44 | }
--------------------------------------------------------------------------------
/listings/src/main/kotlin/chapter09/Listing0914.kts:
--------------------------------------------------------------------------------
1 | @DslMarker
2 | annotation class UserDsl
3 |
--------------------------------------------------------------------------------
/listings/src/main/kotlin/chapter09/Listing0919.kts:
--------------------------------------------------------------------------------
1 | // Android-specific example
2 |
3 | //class AddTodoActivity : AppCompatActivity() {
4 | // // …
5 | // override fun onCreate(savedInstanceState: Bundle?) {
6 | // super.onCreate(savedInstanceState)
7 | // setContentView(createView()) // No inflating of an XML layout
8 | // viewModel = getViewModel(TodoViewModel::class)
9 | // }
10 | //
11 | // private fun createView(): View {
12 | // val linearLayout = LinearLayout(this).apply { // Sets up the linear layout
13 | // orientation = LinearLayout.VERTICAL
14 | // }
15 | // val etNewTodo = EditText(this).apply { // Sets up the EditText
16 | // hint = getString(R.string.enter_new_todo)
17 | // textAppearance = android.R.style.TextAppearance_Medium
18 | // layoutParams = ViewGroup.LayoutParams(
19 | // ViewGroup.LayoutParams.MATCH_PARENT,
20 | // ViewGroup.LayoutParams.WRAP_CONTENT
21 | // )
22 | // }
23 | // val btnAddTodo = Button(this).apply { // Sets up the Button
24 | // text = getString(R.string.add_to_do)
25 | // textAppearance = android.R.style.TextAppearance
26 | // layoutParams = LinearLayout.LayoutParams(
27 | // ViewGroup.LayoutParams.WRAP_CONTENT,
28 | // ViewGroup.LayoutParams.WRAP_CONTENT
29 | // ).apply { gravity = Gravity.CENTER_HORIZONTAL }
30 | // setOnClickListener {
31 | // val newTodo = etNewTodo.text.toString()
32 | // launch(DB) { viewModel.add(TodoItem(newTodo)) }
33 | // finish()
34 | // }
35 | // }
36 | // return linearLayout.apply { // Adds views to the linear layout and returns it
37 | // addView(etNewTodo)
38 | // addView(btnAddTodo)
39 | // }
40 | // }
41 | //}
--------------------------------------------------------------------------------
/listings/src/main/kotlin/chapter09/Listing0920.kts:
--------------------------------------------------------------------------------
1 | // Anko Metadependency for build.gradle
2 |
3 | //def anko_version = "0.10.5"
4 | //implementation "org.jetbrains.anko:anko:$anko_version" // Includes all of Anko
--------------------------------------------------------------------------------
/listings/src/main/kotlin/chapter09/Listing0921.kts:
--------------------------------------------------------------------------------
1 | // More specialized Anko dependencies for build.gradle
2 |
3 | //implementation "org.jetbrains.anko:anko-sdk25:$anko_version"
4 | //implementation "org.jetbrains.anko:anko-sdk25-coroutines:$anko_version"
--------------------------------------------------------------------------------
/listings/src/main/kotlin/chapter09/Listing0922.kts:
--------------------------------------------------------------------------------
1 | // Android-specific example (Anko)
2 |
3 | //verticalLayout {
4 | // button {
5 | // text = "Receive reward"
6 | // onClick { toast("So rewarding!") }
7 | // }
8 | //}
--------------------------------------------------------------------------------
/listings/src/main/kotlin/chapter09/Listing0923.kts:
--------------------------------------------------------------------------------
1 | // Android-specific example (Anko)
2 |
3 | //verticalLayout {
4 | // button { }.lparams(width = matchParent) {
5 | // margin = dip(5)
6 | // }
7 | //}
--------------------------------------------------------------------------------
/listings/src/main/kotlin/chapter09/Listing0924.kts:
--------------------------------------------------------------------------------
1 | // Android-specific example (Anko)
2 |
3 | //class AddTodoActivity : AppCompatActivity() {
4 | // // …
5 | // override fun onCreate(savedInstanceState: Bundle?) {
6 | // super.onCreate(savedInstanceState)
7 | // setContentView(createView()) // Still no inflating of an XML layout
8 | // viewModel = getViewModel(TodoViewModel::class)
9 | // }
10 | //
11 | // private fun createView() = verticalLayout { // Sets up vertical linear layout
12 | //
13 | // val etNewTodo = editText { // Sets up EditText and adds it to the linear layout
14 | // hintResource = R.string.enter_new_todo
15 | // textAppearance = android.R.style.TextAppearance_Medium
16 | // }.lparams(width = matchParent, height = wrapContent) {
17 | // margin = dip(16)
18 | // }
19 | //
20 | // button(R.string.add_to_do) { // Sets up Button and adds it to the linear layout
21 | // textAppearance = android.R.style.TextAppearance
22 | // }.lparams(width = wrapContent, height = wrapContent) {
23 | // gravity = Gravity.CENTER_HORIZONTAL
24 | // }.setOnClickListener { // Could also use onClick inside button {…} instead
25 | // val newTodo = etNewTodo.text.toString()
26 | // launch(DB) { viewModel.add(TodoItem(newTodo)) }
27 | // finish()
28 | // }
29 | // }
30 | //}
--------------------------------------------------------------------------------
/listings/src/main/kotlin/chapter09/Listing0925.kts:
--------------------------------------------------------------------------------
1 | // Android-specific example (Anko)
2 |
3 | //class AddTodoActivity : AppCompatActivity() {
4 | // // …
5 | // override fun onCreate(savedInstanceState: Bundle?) {
6 | // super.onCreate(savedInstanceState)
7 | // setContentView(AddTodoActivityUi().createView(AnkoContext.create(ctx, this)))
8 | // viewModel = getViewModel(TodoViewModel::class)
9 | // }
10 | //
11 | // private inner class AddTodoActivityUi : AnkoComponent {
12 | //
13 | // override fun createView(ui: AnkoContext): View = with(ui) {
14 | //
15 | // verticalLayout {
16 | // val etNewTodo = editText {
17 | // hintResource = R.string.enter_new_todo
18 | // textAppearance = android.R.style.TextAppearance_Medium
19 | // }.lparams(width = matchParent, height = wrapContent) {
20 | // margin = dip(16)
21 | // }
22 | //
23 | // button(R.string.add_to_do) {
24 | // textAppearance = android.R.style.TextAppearance
25 | // }.lparams(width = wrapContent, height = wrapContent) {
26 | // gravity = Gravity.CENTER_HORIZONTAL
27 | // }.setOnClickListener {
28 | // val newTodo = etNewTodo.text.toString()
29 | // launch(DB) { viewModel.add(TodoItem(newTodo)) }
30 | // finish()
31 | // }
32 | // }
33 | // }
34 | // }
35 | //}
--------------------------------------------------------------------------------
/listings/src/main/kotlin/chapter09/Listing0926.kt:
--------------------------------------------------------------------------------
1 | // Android-specific example
2 |
3 | //class SquareFrameLayout(
4 | // context: Context,
5 | // attributes: AttributeSet? = null,
6 | // defStyleAttr: Int = 0
7 | //) : FrameLayout(context, attributes, defStyleAttr) {
8 | //
9 | // override fun onMeasure(widthMeasureSpec: Int, heightMeasureSpec: Int) {
10 | // super.onMeasure(widthMeasureSpec, widthMeasureSpec) // Equal width and height
11 | // }
12 | //}
--------------------------------------------------------------------------------
/listings/src/main/kotlin/chapter09/Listing0927.kt:
--------------------------------------------------------------------------------
1 | // Android-specific example (Anko)
2 |
3 | //import android.view.ViewManager
4 | //import org.jetbrains.anko.custom.ankoView
5 | //
6 | //inline fun ViewManager.squareFrameLayout(init: SquareFrameLayout.() -> Unit) =
7 | // ankoView({ SquareFrameLayout(it) }, theme = 0, init = init)
--------------------------------------------------------------------------------
/listings/src/main/kotlin/chapter09/Listing0928-0941.md:
--------------------------------------------------------------------------------
1 | # Listings for Anko and Gradle Kotlin DSL
2 |
3 | Please find the code for Anko-specific listings and for the Gradle Kotlin DSL in the separate repositories for the apps:
4 | * https://github.com/petersommerhoff/kudoo-app
5 | * https://github.com/petersommerhoff/nutrilicious-app
--------------------------------------------------------------------------------
/listings/src/main/kotlin/chapter10/Listing10.1.kts:
--------------------------------------------------------------------------------
1 | data class Person(val spouse: String?) {
2 | fun getSpouseOrNull() = spouse
3 | }
4 |
5 | fun getPersonOrNull(): Person? = Person(null)
6 | // ----------------
7 |
8 | val person: Person? = getPersonOrNull()
9 |
10 | if (person != null) {
11 | person.getSpouseOrNull() // Let's say returns null
12 | } else {
13 | println("No person found (if/else)") // Not printed
14 | }
15 |
16 | person?.let {
17 | person.getSpouseOrNull() // Let's say returns null
18 | } ?: println("No person found (let)") // Printed
19 |
--------------------------------------------------------------------------------