├── 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 | 6 | 7 | 8 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /exercises/.idea/codeStyles/codeStyleConfig.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 5 | -------------------------------------------------------------------------------- /exercises/.idea/encodings.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /exercises/.idea/kotlinc.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 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> 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 | 6 | 7 | 9 | 10 | 14 | 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 | 7 | 8 | 9 | 10 | -------------------------------------------------------------------------------- /listings/.idea/modules/chapter02_main.iml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 20 | 23 | 25 | 26 | 27 | 28 | -------------------------------------------------------------------------------- /listings/.idea/modules/chapter02_test.iml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 20 | 23 | 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 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 | --------------------------------------------------------------------------------