├── .github └── workflows │ └── verify-samples.yml ├── .idea ├── misc.xml └── modules.xml ├── LICENSE.txt ├── README.md └── examples ├── 01_introduction ├── 00_description.md ├── 01_Hello world.md ├── 02_Functions.md ├── 03_Variables.md ├── 04_Null Safety.md ├── 05_Classes.md ├── 06_Generics.md └── 07_Inheritance.md ├── 02_control_flow ├── 00_description.md ├── 01_When.md ├── 02_Loops.md ├── 03_Ranges.md ├── 04_Equality Checks.md └── 05_Conditional expression.md ├── 03_special_classes ├── 00_description.md ├── 01_Data classes.md ├── 02_Enum.md ├── 03_Sealed Classes.md └── 04_Object.md ├── 04_functional ├── 00_description.md ├── 01_Higher-Order Functions.md ├── 02_Lambdas.md └── 03_extensionFunctions.md ├── 05_Collections ├── 00_description.md ├── 01_List.md ├── 02_Set.md ├── 03_Map.md ├── 04_filter.md ├── 04_map.md ├── 05_existential.md ├── 06_find.md ├── 07_firstlast.md ├── 08_count.md ├── 10_associateBy.md ├── 11_partition.md ├── 12_flatMap.md ├── 13_max.md ├── 14_sorted.md ├── 15_Map_getValue.md ├── 16_zip.md └── 17_getOrElse.md ├── 06_scope_functions ├── 00_description.md ├── 01_let.md ├── 02_run.md ├── 03_with.md ├── 04_apply.md └── 05_also.md ├── 07_Delegation ├── 00_description.md ├── 01_delegationPattern.md └── 02_DelegatedProperties.md ├── 08_productivity_boosters ├── 00_description.md ├── 01_namedArguments.md ├── 02_String Templates.md ├── 03_Destructuring Declarations.md └── 04_Smart Casts.md └── 09_Kotlin_JS ├── 00_description.md ├── 01_dynamic.md ├── 02_js_function.md ├── 03_external.md ├── 05_Canvas.md └── 06_HtmlBuilder.md /.github/workflows/verify-samples.yml: -------------------------------------------------------------------------------- 1 | name: Verify samples 2 | 3 | on: 4 | push: 5 | branches: [ master ] 6 | pull_request: 7 | branches: [ master ] 8 | jobs: 9 | build: 10 | runs-on: ubuntu-latest 11 | steps: 12 | - name: Set up JDK 13 | uses: actions/setup-java@v2 14 | with: 15 | java-version: '11' 16 | distribution: 'adopt' 17 | - name: Run a verifier 18 | uses: AlexanderPrendota/kotlin-samples-verifier@master 19 | with: 20 | push-repository: 'https://github.com/AlexanderPrendota/kotlin-compiler-server' 21 | tag-filter: '#tag="code" & class="language-run-kotlin" & !validate="false"' 22 | push-path: 'src/test/resources/test-compile-data/jvm/kotlin-by-example' 23 | username: '${{ secrets.KOTLIN_BY_EXAMPLE_TOKEN }}' #token with an access to create PR in push-repository and issue in this repository 24 | 25 | -------------------------------------------------------------------------------- /.idea/misc.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 6 | -------------------------------------------------------------------------------- /.idea/modules.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /LICENSE.txt: -------------------------------------------------------------------------------- 1 | Copyright (c) 2017 JetBrains 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy 4 | of this software and associated documentation files (the "Software"), to deal 5 | in the Software without restriction, including without limitation the rights 6 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | copies of the Software, and to permit persons to whom the Software is 8 | furnished to do so, subject to the following conditions: 9 | 10 | The above copyright notice and this permission notice shall be included in all 11 | copies or substantial portions of the Software. 12 | 13 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 19 | SOFTWARE. -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Kotlin by Example 2 | [![official JetBrains project](https://jb.gg/badges/official-plastic.svg)](https://github.com/JetBrains#jetbrains-on-github) 3 | 4 | This is sources of [Kotlin Examples](https://play.kotlinlang.org/byExample/overview). 5 | 6 | ### How to make example 7 | 8 | Create markdown file in `examples` folder. 9 | 10 | ```md 11 | # Header 12 | 13 | Lorem ipsum dolor sit `amet`, consectetur adipisicing elit. Aspernatur, molestias, velit? 14 | 15 | `​`​`run-kotlin 16 | fun main(args: Array) { 17 | println("Hello from template") 18 | } 19 | `​`​` 20 | 21 | 1. Epsum `factorial` non deposit quid pro quo hic escorol. 22 | 2. Souvlaki ignitus carborundum e pluribus unum. 23 | 3. Lorem ipsum dolor `sit` amet, consectetur adipisicing elit. Dicta ipsa ipsam odio officiis repellat suscipit unde vel voluptatibus. Dolorum esse eum fugit nihil provident quae quaerat quidem reiciendis, repudiandae ullam. 24 | 4. Quote meon an `estimate` et non interruptus stadium. 25 | 5. Quote meon an `estimate` 26 | 27 | 28 | | Tables | Are | Cool | 29 | | ------------- |:-------------:| -----:| 30 | | col 3 is | right-aligned | 10000 | 31 | | col 2 is | centered | 100 | 32 | | zebra stripes | are neat | 11 | 33 | 34 | ### Links 35 | 36 | See more information in our [website](https://kotlinlang.org/) 37 | ``` 38 | Render as: 39 | 40 | screen shot 2018-08-17 at 2 48 04 pm 41 | 42 | How to use different attributes on runnable examples see [Kotlin Playground documentation](https://github.com/JetBrains/kotlin-playground/blob/master/README.md). 43 | 44 | ### Guidelines 45 | 46 | 1. Try and keep text to a minimum. Remember, this site is not trying to replace documentation nor necessarily describe all edge cases or dive into details. Rule of thumb should be a single paragraph introducing the example. Rest should be line annotations. 47 | 2. Use 4 spaces for tabs. 48 | 3. Make sure examples compile and also have some output when they run. 49 | 50 | ### How to contribute 51 | 52 | 1. Fork & clone our repository. 53 | 2. Create new chapter with examples or add new example to existing chapter. 54 | 1. Create `chapter` folder in `examples` folder. 55 | 2. Create `description.md` file in your chapter and set a header — it's a chapter name! 56 | 3. Create examples files. 57 | 58 | Path to your example will be `https://host/byExample/chapter/myExample1` 59 | 60 | 61 | *Have a nice Kotlin!* 62 | -------------------------------------------------------------------------------- /examples/01_introduction/00_description.md: -------------------------------------------------------------------------------- 1 | # Introduction 2 | -------------------------------------------------------------------------------- /examples/01_introduction/01_Hello world.md: -------------------------------------------------------------------------------- 1 | # Hello World 2 | 3 | ```run-kotlin 4 | package org.kotlinlang.play // 1 5 | 6 | fun main() { // 2 7 | println("Hello, World!") // 3 8 | } 9 | ``` 10 | 11 | 1. Kotlin code is usually defined in packages. Package specification is optional: If you don't specify a package in a source file, its content goes to the default package. 12 | 2. An entry point to a Kotlin application is the `main` function. You can declare it without any parameters. The return type is not specified, which means that the function returns nothing. 13 | 3. `println` writes a line to the standard output. It is imported implicitly. Also, note that semicolons at the end of code lines are optional. 14 | 15 | -------------------------------------------------------------------------------- /examples/01_introduction/02_Functions.md: -------------------------------------------------------------------------------- 1 | # Functions 2 | 3 | ### Default Parameter Values and Named Arguments 4 | 5 | ```run-kotlin 6 | fun printMessage(message: String): Unit { // 1 7 | println(message) 8 | } 9 | 10 | fun printMessageWithPrefix(message: String, prefix: String = "Info") { // 2 11 | println("[$prefix] $message") 12 | } 13 | 14 | fun sum(x: Int, y: Int): Int { // 3 15 | return x + y 16 | } 17 | 18 | fun multiply(x: Int, y: Int) = x * y // 4 19 | 20 | fun main() { 21 | printMessage("Hello") // 5 22 | printMessageWithPrefix("Hello", "Log") // 6 23 | printMessageWithPrefix("Hello") // 7 24 | printMessageWithPrefix(prefix = "Log", message = "Hello") // 8 25 | println(sum(1, 2)) // 9 26 | println(multiply(2, 4)) // 10 27 | } 28 | ``` 29 | 30 | 1. A simple function that takes a parameter of type `String` and returns `Unit` (i.e., no return value). 31 | 2. A function that takes a second [optional parameter with default value](https://kotlinlang.org/docs/reference/functions.html#default-arguments) `Info`. The return type is omitted, meaning that it's actually `Unit`. 32 | 3. A function that returns an integer. 33 | 4. A single-expression function that returns an integer (inferred). 34 | 5. Calls the first function with the argument `Hello`. 35 | 6. Calls the function with two parameters, passing values for both of them. 36 | 7. Calls the same function omitting the second one. The default value `Info` is used. 37 | 8. Calls the same function using [named arguments](https://kotlinlang.org/docs/reference/functions.html#named-arguments) and changing the order of the arguments. 38 | 9. Prints the result of the `sum` function call. 39 | 10. Prints the result of the `multiply` function call. 40 | 41 | ### Infix Functions 42 | 43 | Member functions and extensions with a single parameter can be turned into [infix functions](https://kotlinlang.org/docs/reference/functions.html#infix-notation). 44 | 45 | ```run-kotlin 46 | fun main() { 47 | 48 | infix fun Int.times(str: String) = str.repeat(this) // 1 49 | println(2 times "Bye ") // 2 50 | 51 | val pair = "Ferrari" to "Katrina" // 3 52 | println(pair) 53 | 54 | infix fun String.onto(other: String) = Pair(this, other) // 4 55 | val myPair = "McLaren" onto "Lucas" 56 | println(myPair) 57 | 58 | val sophia = Person("Sophia") 59 | val claudia = Person("Claudia") 60 | sophia likes claudia // 5 61 | } 62 | 63 | class Person(val name: String) { 64 | val likedPeople = mutableListOf() 65 | infix fun likes(other: Person) { likedPeople.add(other) } // 6 66 | } 67 | ``` 68 | 69 | 1. Defines an infix extension function on `Int`. 70 | 2. Calls the infix function. 71 | 3. Creates a `Pair` by calling the infix function `to` from the standard library. 72 | 4. Here's your own implementation of `to` creatively called `onto`. 73 | 5. Infix notation also works on members functions (methods). 74 | 6. The containing class becomes the first parameter. 75 | 76 | Note that the example uses [local functions](https://kotlinlang.org/docs/reference/functions.html#local-functions) (functions nested within another function). 77 | 78 | ### Operator Functions 79 | 80 | Certain functions can be "upgraded" to [operators](https://kotlinlang.org/docs/reference/operator-overloading.html), allowing their calls with the corresponding operator symbol. 81 | 82 | ```run-kotlin 83 | fun main() { 84 | //sampleStart 85 | operator fun Int.times(str: String) = str.repeat(this) // 1 86 | println(2 * "Bye ") // 2 87 | 88 | operator fun String.get(range: IntRange) = substring(range) // 3 89 | val str = "Always forgive your enemies; nothing annoys them so much." 90 | println(str[0..14]) // 4 91 | //sampleEnd 92 | } 93 | ``` 94 | 95 | 1. This takes the infix function from above one step further using the `operator` modifier. 96 | 2. The operator symbol for `times()` is `*` so that you can call the function using `2 * "Bye"`. 97 | 3. An operator function allows easy range access on strings. 98 | 4. The `get()` operator enables [bracket-access syntax](https://kotlinlang.org/docs/reference/operator-overloading.html#indexed). 99 | 100 | ### Functions with `vararg` Parameters 101 | 102 | [Varargs](https://kotlinlang.org/docs/reference/functions.html#variable-number-of-arguments-varargs) allow you to pass any number of arguments by separating them with commas. 103 | 104 | ```run-kotlin 105 | fun main() { 106 | //sampleStart 107 | fun printAll(vararg messages: String) { // 1 108 | for (m in messages) println(m) 109 | } 110 | printAll("Hello", "Hallo", "Salut", "Hola", "你好") // 2 111 | 112 | fun printAllWithPrefix(vararg messages: String, prefix: String) { // 3 113 | for (m in messages) println(prefix + m) 114 | } 115 | printAllWithPrefix( 116 | "Hello", "Hallo", "Salut", "Hola", "你好", 117 | prefix = "Greeting: " // 4 118 | ) 119 | 120 | fun log(vararg entries: String) { 121 | printAll(*entries) // 5 122 | } 123 | log("Hello", "Hallo", "Salut", "Hola", "你好") 124 | //sampleEnd 125 | } 126 | ``` 127 | 128 | 1. The `vararg` modifier turns a parameter into a vararg. 129 | 2. This allows calling `printAll` with any number of string arguments. 130 | 3. Thanks to named parameters, you can even add another parameter of the same type after the vararg. This wouldn't be allowed in Java because there's no way to pass a value. 131 | 4. Using named parameters, you can set a value to `prefix` separately from the vararg. 132 | 5. At runtime, a vararg is just an array. To pass it along into a vararg parameter, use the special spread operator `*` that lets you pass in `*entries` (a vararg of `String`) instead of `entries` (an `Array`). 133 | -------------------------------------------------------------------------------- /examples/01_introduction/03_Variables.md: -------------------------------------------------------------------------------- 1 | # Variables 2 | 3 | Kotlin has powerful type inference. While you can explicitly declare the type of a variable, you'll usually let the 4 | compiler do the work by inferring it. Kotlin does not enforce immutability, though it is recommended. 5 | In essence use *val* over *var*. 6 | 7 | ```run-kotlin 8 | fun main() { 9 | //sampleStart 10 | var a: String = "initial" // 1 11 | println(a) 12 | val b: Int = 1 // 2 13 | val c = 3 // 3 14 | //sampleEnd 15 | } 16 | ``` 17 | 18 | 1. Declares a mutable variable and initializes it. 19 | 2. Declares an immutable variable and initializes it. 20 | 3. Declares an immutable variable and initializes it without specifying the type. The compiler infers the type `Int`. 21 | 22 | ```run-kotlin 23 | fun main() { 24 | //sampleStart 25 | var e: Int // 1 26 | println(e) // 2 27 | //sampleEnd 28 | } 29 | ``` 30 | {validate="false"} 31 | 32 | 1. Declares a variable without initialization. 33 | 2. An attempt to use the variable causes a compiler error: `Variable 'e' must be initialized`. 34 | 35 | You're free to choose when you initialize a variable, however, it must be initialized before the first read. 36 | 37 | ```run-kotlin 38 | fun someCondition() = true 39 | 40 | fun main() { 41 | //sampleStart 42 | val d: Int // 1 43 | 44 | if (someCondition()) { 45 | d = 1 // 2 46 | } else { 47 | d = 2 // 2 48 | } 49 | 50 | println(d) // 3 51 | //sampleEnd 52 | } 53 | ``` 54 | 55 | 1. Declares a variable without initialization. 56 | 2. Initializes the variable with different values depending on some condition. 57 | 3. Reading the variable is possible because it's already been initialized. 58 | -------------------------------------------------------------------------------- /examples/01_introduction/04_Null Safety.md: -------------------------------------------------------------------------------- 1 | # Null Safety 2 | 3 | In an effort to rid the world of `NullPointerException`, variable types in Kotlin don't allow the assignment of `null`. If you need a variable that can be null, declare it nullable by adding `?` at the end of its type. 4 | 5 | ```run-kotlin 6 | fun main() { 7 | //sampleStart 8 | var neverNull: String = "This can't be null" // 1 9 | 10 | neverNull = null // 2 11 | 12 | var nullable: String? = "You can keep a null here" // 3 13 | 14 | nullable = null // 4 15 | 16 | var inferredNonNull = "The compiler assumes non-null" // 5 17 | 18 | inferredNonNull = null // 6 19 | 20 | fun strLength(notNull: String): Int { // 7 21 | return notNull.length 22 | } 23 | 24 | strLength(neverNull) // 8 25 | strLength(nullable) // 9 26 | //sampleEnd 27 | } 28 | ``` 29 | {validate="false"} 30 | 31 | 1. Declares a non-`null` String variable. 32 | 2. When trying to assign `null` to non-nullable variable, a compilation error is produced. 33 | 3. Declares a nullable String variable. 34 | 4. Sets the `null` value to the nullable variable. This is OK. 35 | 5. When inferring types, the compiler assumes non-`null` for variables that are initialized with a value. 36 | 6. When trying to assign `null` to a variable with inferred type, a compilation error is produced. 37 | 7. Declares a function with a non-`null` string parameter. 38 | 8. Calls the function with a `String` (non-nullable) argument. This is OK. 39 | 9. When calling the function with a `String?` (nullable) argument, a compilation error is produced. 40 | 41 | ## Working with Nulls 42 | 43 | Sometimes Kotlin programs need to work with null values, such as when interacting with external Java code or 44 | representing a truly absent state. Kotlin provides null tracking to elegantly deal with such situations. 45 | 46 | ```run-kotlin 47 | //sampleStart 48 | fun describeString(maybeString: String?): String { // 1 49 | if (maybeString != null && maybeString.length > 0) { // 2 50 | return "String of length ${maybeString.length}" 51 | } else { 52 | return "Empty or null string" // 3 53 | } 54 | } 55 | //sampleEnd 56 | 57 | fun main() { 58 | println(describeString(null)) 59 | } 60 | ``` 61 | 62 | 1. A function that takes in a nullable string and returns its description. 63 | 2. If the given string is not `null` and not empty, return information about its length. 64 | 3. Otherwise, tell the caller that the string is empty or null. 65 | 66 | 67 | 68 | -------------------------------------------------------------------------------- /examples/01_introduction/05_Classes.md: -------------------------------------------------------------------------------- 1 | # Classes 2 | 3 | The [class declaration](https://kotlinlang.org/docs/reference/classes.html#classes) consists of the class name, the class header (specifying its type parameters, 4 | the primary constructor etc.) and the class body, surrounded by curly braces. 5 | Both the header and the body are optional; if the class has no body, curly braces can be omitted. 6 | 7 | ```run-kotlin 8 | class Customer // 1 9 | 10 | class Contact(val id: Int, var email: String) // 2 11 | 12 | fun main() { 13 | 14 | val customer = Customer() // 3 15 | 16 | val contact = Contact(1, "mary@gmail.com") // 4 17 | 18 | println(contact.id) // 5 19 | contact.email = "jane@gmail.com" // 6 20 | } 21 | ``` 22 | 23 | 1. Declares a class named `Customer` without any properties or user-defined constructors. A non-parameterized default constructor is created by Kotlin automatically. 24 | 2. Declares a class with two properties: immutable `id` and mutable `email`, and a constructor with two parameters `id` and `email`. 25 | 3. Creates an instance of the class `Customer` via the default constructor. Note that there is no `new` keyword in Kotlin. 26 | 4. Creates an instance of the class `Contact` using the constructor with two arguments. 27 | 5. Accesses the property `id`. 28 | 6. Updates the value of the property `email`. 29 | -------------------------------------------------------------------------------- /examples/01_introduction/06_Generics.md: -------------------------------------------------------------------------------- 1 | # Generics 2 | 3 | [Generics](https://kotlinlang.org/docs/reference/generics.html) are a genericity mechanism that's become standard in modern languages. Generic classes and functions increase code reusability by encapsulating common logic that is independent of a particular generic type, like the logic inside a `List` is independent of what `T` is. 4 | 5 | ### Generic Classes 6 | 7 | The first way to use generics in Kotlin is creating generic classes. 8 | 9 | ```run-kotlin 10 | //sampleStart 11 | class MutableStack(vararg items: E) { // 1 12 | 13 | private val elements = items.toMutableList() 14 | 15 | fun push(element: E) = elements.add(element) // 2 16 | 17 | fun peek(): E = elements.last() // 3 18 | 19 | fun pop(): E = elements.removeAt(elements.size - 1) 20 | 21 | fun isEmpty() = elements.isEmpty() 22 | 23 | fun size() = elements.size 24 | 25 | override fun toString() = "MutableStack(${elements.joinToString()})" 26 | } 27 | //sampleEnd 28 | 29 | fun main() { 30 | val stack = MutableStack(0.62, 3.14, 2.7) 31 | stack.push(9.87) 32 | println(stack) 33 | 34 | println("peek(): ${stack.peek()}") 35 | println(stack) 36 | 37 | for (i in 1..stack.size()) { 38 | println("pop(): ${stack.pop()}") 39 | println(stack) 40 | } 41 | } 42 | 43 | ``` 44 | 45 | 46 | 1. Defines a generic class `MutableStack` where `E` is called the _generic type parameter_. At use-site, it is assigned to a specific type such as `Int` by declaring a `MutableStack`. 47 | 2. Inside the generic class, `E` can be used as a parameter like any other type. 48 | 3. You can also use `E` as a return type. 49 | 50 | Note that the implementation makes heavy use of Kotlin's shorthand syntax for functions that can be defined in a single expression. 51 | 52 | 53 | ### Generic Functions 54 | 55 | You can also [generify functions](https://kotlinlang.org/docs/reference/generics.html#generic-functions) if their logic is independent of a specific type. For instance, you can write a utility function to create mutable stacks: 56 | 57 | 58 | ```run-kotlin 59 | class MutableStack(vararg items: E) { // 1 60 | 61 | private val elements = items.toMutableList() 62 | 63 | fun push(element: E) = elements.add(element) // 2 64 | 65 | fun peek(): E = elements.last() // 3 66 | 67 | fun pop(): E = elements.removeAt(elements.size - 1) 68 | 69 | fun isEmpty() = elements.isEmpty() 70 | 71 | fun size() = elements.size 72 | 73 | override fun toString() = "MutableStack(${elements.joinToString()})" 74 | } 75 | 76 | //sampleStart 77 | fun mutableStackOf(vararg elements: E) = MutableStack(*elements) 78 | 79 | fun main() { 80 | val stack = mutableStackOf(0.62, 3.14, 2.7) 81 | println(stack) 82 | } 83 | //sampleEnd 84 | ``` 85 | 86 | Note that the compiler can infer the generic type from the parameters of `mutableStackOf` so that you don't have to write `mutableStackOf(...)`. 87 | -------------------------------------------------------------------------------- /examples/01_introduction/07_Inheritance.md: -------------------------------------------------------------------------------- 1 | # Inheritance 2 | 3 | Kotlin fully supports the traditional object-oriented inheritance mechanism. 4 | 5 | ```run-kotlin 6 | open class Dog { // 1 7 | open fun sayHello() { // 2 8 | println("wow wow!") 9 | } 10 | } 11 | 12 | class Yorkshire : Dog() { // 3 13 | override fun sayHello() { // 4 14 | println("wif wif!") 15 | } 16 | } 17 | 18 | fun main() { 19 | val dog: Dog = Yorkshire() 20 | dog.sayHello() 21 | } 22 | ``` 23 | 24 | 1. Kotlin classes are _final_ by default. If you want to allow the class inheritance, mark the class with the `open` modifier. 25 | 2. Kotlin methods are also _final_ by default. As with the classes, the `open` modifier allows overriding them. 26 | 3. A class inherits a superclass when you specify the `: SuperclassName()` after its name. The empty parentheses `()` indicate an invocation of the superclass default constructor. 27 | 4. Overriding methods or attributes requires the `override` modifier. 28 | 29 | ### Inheritance with Parameterized Constructor 30 | 31 | ```run-kotlin 32 | //sampleStart 33 | open class Tiger(val origin: String) { 34 | fun sayHello() { 35 | println("A tiger from $origin says: grrhhh!") 36 | } 37 | } 38 | 39 | class SiberianTiger : Tiger("Siberia") // 1 40 | 41 | fun main() { 42 | val tiger: Tiger = SiberianTiger() 43 | tiger.sayHello() 44 | } 45 | //sampleEnd 46 | ``` 47 | 48 | 49 | 1. If you want to use a parameterized constructor of the superclass when creating a subclass, provide the constructor arguments in the subclass declaration. 50 | 51 | 52 | ### Passing Constructor Arguments to Superclass 53 | 54 | ```run-kotlin 55 | //sampleStart 56 | open class Lion(val name: String, val origin: String) { 57 | fun sayHello() { 58 | println("$name, the lion from $origin says: graoh!") 59 | } 60 | } 61 | 62 | class Asiatic(name: String) : Lion(name = name, origin = "India") // 1 63 | 64 | fun main() { 65 | val lion: Lion = Asiatic("Rufo") // 2 66 | lion.sayHello() 67 | } 68 | //sampleEnd 69 | ``` 70 | 71 | 1. `name` in the `Asiatic` declaration is neither a `var` nor `val`: it's a constructor argument, whose value is passed to the `name` property of the superclass `Lion`. 72 | 2. Creates an `Asiatic` instance with the name `Rufo`. The call invokes the `Lion` constructor with arguments `Rufo` and `India`. 73 | 74 | -------------------------------------------------------------------------------- /examples/02_control_flow/00_description.md: -------------------------------------------------------------------------------- 1 | # Control Flow 2 | -------------------------------------------------------------------------------- /examples/02_control_flow/01_When.md: -------------------------------------------------------------------------------- 1 | # When 2 | 3 | Instead of the widely used `switch` statement, Kotlin provides the more flexible and clear `when` construction. It can be used either as a statement or as an expression. 4 | 5 | ## When Statement 6 | 7 | ```run-kotlin 8 | fun main() { 9 | cases("Hello") 10 | cases(1) 11 | cases(0L) 12 | cases(MyClass()) 13 | cases("hello") 14 | } 15 | 16 | fun cases(obj: Any) { 17 | when (obj) { // 1 18 | 1 -> println("One") // 2 19 | "Hello" -> println("Greeting") // 3 20 | is Long -> println("Long") // 4 21 | !is String -> println("Not a string") // 5 22 | else -> println("Unknown") // 6 23 | } 24 | } 25 | 26 | class MyClass 27 | ``` 28 | 29 | 1. This is a `when` statement. 30 | 2. Checks whether `obj` equals to `1`. 31 | 3. Checks whether `obj` equals to `"Hello"`. 32 | 4. Performs type checking. 33 | 5. Performs inverse type checking. 34 | 6. Default statement (might be omitted). 35 | 36 | Note that all branch conditions are checked sequentially until one of them is satisfied. So, only the first suitable branch will be executed. 37 | 38 | ## When Expression 39 | 40 | ```run-kotlin 41 | fun main() { 42 | println(whenAssign("Hello")) 43 | println(whenAssign(3.4)) 44 | println(whenAssign(1)) 45 | println(whenAssign(MyClass())) 46 | } 47 | 48 | fun whenAssign(obj: Any): Any { 49 | val result = when (obj) { // 1 50 | 1 -> "one" // 2 51 | "Hello" -> 1 // 3 52 | is Long -> false // 4 53 | else -> 42 // 5 54 | } 55 | return result 56 | } 57 | 58 | class MyClass 59 | ``` 60 | 61 | 62 | 1. This is a `when` expression. 63 | 2. Sets the value to `"one"` if `obj` equals to `1`. 64 | 3. Sets the value to one if `obj` equals to `"Hello"`. 65 | 4. Sets the value to `false` if `obj` is an instance of `Long`. 66 | 5. Sets the value `42` if none of the previous conditions are satisfied. Unlike in `when` _statement_, the default branch is usually required in `when` _expression_, except the case when the compiler can check that other branches cover all possible cases. 67 | -------------------------------------------------------------------------------- /examples/02_control_flow/02_Loops.md: -------------------------------------------------------------------------------- 1 | # Loops 2 | 3 | Kotlin supports all the commonly used loops: `for`, `while`, `do-while` 4 | 5 | ### `for` 6 | 7 | `for` in Kotlin works the same way as in most languages. 8 | 9 | ```run-kotlin 10 | fun main(args: Array) { 11 | //sampleStart 12 | val cakes = listOf("carrot", "cheese", "chocolate") 13 | 14 | for (cake in cakes) { // 1 15 | println("Yummy, it's a $cake cake!") 16 | } 17 | 18 | //sampleEnd 19 | } 20 | ``` 21 | 22 | 1. Loops through each cake in the list. 23 | 24 | ### `while` and `do-while` 25 | 26 | `while` and `do-while` constructs work similarly to most languages as well. 27 | 28 | ```run-kotlin 29 | fun eatACake() = println("Eat a Cake") 30 | fun bakeACake() = println("Bake a Cake") 31 | 32 | fun main(args: Array) { 33 | var cakesEaten = 0 34 | var cakesBaked = 0 35 | 36 | while (cakesEaten < 5) { // 1 37 | eatACake() 38 | cakesEaten ++ 39 | } 40 | 41 | do { // 2 42 | bakeACake() 43 | cakesBaked++ 44 | } while (cakesBaked < cakesEaten) 45 | 46 | } 47 | ``` 48 | 49 | 1. Executes the block while the condition is true. 50 | 2. Executes the block first and then checking the condition. 51 | 52 | ### Iterators 53 | 54 | You can define your own iterators in your classes by implementing the `iterator` operator in them. 55 | 56 | ```run-kotlin 57 | class Animal(val name: String) 58 | 59 | class Zoo(val animals: List) { 60 | 61 | operator fun iterator(): Iterator { // 1 62 | return animals.iterator() // 2 63 | } 64 | } 65 | 66 | fun main() { 67 | 68 | val zoo = Zoo(listOf(Animal("zebra"), Animal("lion"))) 69 | 70 | for (animal in zoo) { // 3 71 | println("Watch out, it's a ${animal.name}") 72 | } 73 | 74 | } 75 | ``` 76 | 77 | 1. Defines an iterator in a class. It must be named `iterator` and have the `operator` modifier. 78 | 2. Returns the iterator that meets the following method requirements: 79 | * `next()`: `Animal` 80 | * `hasNext()`: `Boolean` 81 | 3. Loops through animals in the zoo with the user-defined iterator. 82 | 83 | The iterator can be declared in the type or as an extension function. 84 | -------------------------------------------------------------------------------- /examples/02_control_flow/03_Ranges.md: -------------------------------------------------------------------------------- 1 | # Ranges 2 | 3 | There is a set of tools for defining ranges in Kotlin. Let's have a brief look at them. 4 | 5 | ```run-kotlin 6 | fun main() { 7 | //sampleStart 8 | for(i in 0..3) { // 1 9 | print(i) 10 | } 11 | print(" ") 12 | 13 | for(i in 0 until 3) { // 2 14 | print(i) 15 | } 16 | print(" ") 17 | 18 | for(i in 2..8 step 2) { // 3 19 | print(i) 20 | } 21 | print(" ") 22 | 23 | for (i in 3 downTo 0) { // 4 24 | print(i) 25 | } 26 | print(" ") 27 | 28 | //sampleEnd 29 | } 30 | ``` 31 | 32 | 1. Iterates over a range starting from 0 up to 3 (inclusive). Like 'for(i=0; i<=3; ++i)' in other programming languages (C/C++/Java). 33 | 2. Iterates over a range starting from 0 up to 3 (exclusive). Like for loop in Python or like 'for(i=0; i<3; ++i)' in other programming languages (C/C++/Java). 34 | 3. Iterates over a range with a custom increment step for consecutive elements. 35 | 4. Iterates over a range in _reverse_ order. 36 | 37 | Char ranges are also supported: 38 | 39 | ```run-kotlin 40 | fun main() { 41 | //sampleStart 42 | for (c in 'a'..'d') { // 1 43 | print(c) 44 | } 45 | print(" ") 46 | 47 | for (c in 'z' downTo 's' step 2) { // 2 48 | print(c) 49 | } 50 | print(" ") 51 | 52 | //sampleEnd 53 | } 54 | ``` 55 | 56 | 1. Iterates over a char range in alphabetical order. 57 | 2. Char ranges support `step` and `downTo` as well. 58 | 59 | Ranges are also useful in `if` statements: 60 | 61 | ```run-kotlin 62 | fun main() { 63 | //sampleStart 64 | val x = 2 65 | if (x in 1..5) { // 1 66 | print("x is in range from 1 to 5") 67 | } 68 | println() 69 | 70 | if (x !in 6..10) { // 2 71 | print("x is not in range from 6 to 10") 72 | } 73 | //sampleEnd 74 | } 75 | ``` 76 | 77 | 1. Checks if a value is in the range. 78 | 2. `!in` is the opposite of `in`. 79 | -------------------------------------------------------------------------------- /examples/02_control_flow/04_Equality Checks.md: -------------------------------------------------------------------------------- 1 | # Equality Checks 2 | 3 | Kotlin uses `==` for structural comparison and `===` for referential comparison. 4 | 5 | More precisely, `a == b` compiles down to `if (a == null) b == null else a.equals(b)`. 6 | 7 | ```run-kotlin 8 | fun main() { 9 | //sampleStart 10 | 11 | val authors = setOf("Shakespeare", "Hemingway", "Twain") 12 | val writers = setOf("Twain", "Shakespeare", "Hemingway") 13 | 14 | println(authors == writers) // 1 15 | println(authors === writers) // 2 16 | //sampleEnd 17 | } 18 | ``` 19 | 20 | 1. Returns `true` because it calls `authors.equals(writers)` and sets ignore element order. 21 | 2. Returns `false` because `authors` and `writers` are distinct references. 22 | -------------------------------------------------------------------------------- /examples/02_control_flow/05_Conditional expression.md: -------------------------------------------------------------------------------- 1 | # Conditional Expression 2 | 3 | There is no ternary operator `condition ? then : else` in Kotlin. Instead, `if` may be used as an expression: 4 | 5 | ```run-kotlin 6 | fun main() { 7 | //sampleStart 8 | fun max(a: Int, b: Int) = if (a > b) a else b // 1 9 | 10 | println(max(99, -42)) 11 | //sampleEnd 12 | } 13 | ``` 14 | 15 | 1. `if` is an expression here: it returns a value. 16 | -------------------------------------------------------------------------------- /examples/03_special_classes/00_description.md: -------------------------------------------------------------------------------- 1 | # Special Classes 2 | -------------------------------------------------------------------------------- /examples/03_special_classes/01_Data classes.md: -------------------------------------------------------------------------------- 1 | # Data Classes 2 | 3 | [Data classes](https://kotlinlang.org/docs/reference/data-classes.html) make it easy to create classes that are used to store values. Such classes are automatically provided with methods for copying, getting a string representation, and using instances in collections. You can override these methods with your own implementations in the class declaration. 4 | 5 | ```run-kotlin 6 | data class User(val name: String, val id: Int) { // 1 7 | override fun equals(other: Any?) = 8 | other is User && other.id == this.id // 2 9 | } 10 | fun main() { 11 | val user = User("Alex", 1) 12 | println(user) // 3 13 | 14 | val secondUser = User("Alex", 1) 15 | val thirdUser = User("Max", 2) 16 | 17 | println("user == secondUser: ${user == secondUser}") // 4 18 | println("user == thirdUser: ${user == thirdUser}") 19 | 20 | // hashCode() function 21 | println(user.hashCode()) // 5 22 | println(secondUser.hashCode()) 23 | println(thirdUser.hashCode()) 24 | 25 | // copy() function 26 | println(user.copy()) // 6 27 | println(user === user.copy()) // 7 28 | println(user.copy("Max")) // 8 29 | println(user.copy(id = 3)) // 9 30 | 31 | println("name = ${user.component1()}") // 10 32 | println("id = ${user.component2()}") 33 | } 34 | ``` 35 | 36 | 1. Defines a data class with the `data` modifier. 37 | 2. Override the default `equals` method by declaring users equal if they have the same `id`. 38 | 3. Method `toString` is auto-generated, which makes `println` output look nice. 39 | 4. Our custom `equals` considers two instances equal if their `id`s are equal. 40 | 5. Data class instances with exactly matching attributes have the same `hashCode`. 41 | 6. Auto-generated `copy` function makes it easy to create a new instance. 42 | 7. `copy` creates a new instance, so the object and its copy have distinct references. 43 | 8. When copying, you can change values of certain properties. `copy` accepts arguments in the same order as the class constructor. 44 | 9. Use `copy` with named arguments to change the value despite of the properties order. 45 | 10. Auto-generated `componentN` functions let you get the values of properties in the order of declaration. 46 | -------------------------------------------------------------------------------- /examples/03_special_classes/02_Enum.md: -------------------------------------------------------------------------------- 1 | # Enum Classes 2 | 3 | [Enum classes](https://kotlinlang.org/docs/reference/enum-classes.html) are used to model types that represent a finite set of distinct values, such as directions, states, modes, and so forth. 4 | 5 | ```run-kotlin 6 | enum class State { 7 | IDLE, RUNNING, FINISHED // 1 8 | } 9 | 10 | fun main() { 11 | val state = State.RUNNING // 2 12 | val message = when (state) { // 3 13 | State.IDLE -> "It's idle" 14 | State.RUNNING -> "It's running" 15 | State.FINISHED -> "It's finished" 16 | } 17 | println(message) 18 | } 19 | 20 | ``` 21 | 22 | 1. Defines a simple enum class with three enum constants. The number of constants is always finite and they are all distinct. 23 | 2. Accesses an enum constant via the class name. 24 | 3. With enums, the compiler can infer if a `when`-expression is exhaustive so that you don't need the `else`-case. 25 | 26 | Enums may contain properties and methods like other classes, separated from the list of enum constants by a semicolon. 27 | 28 | ```run-kotlin 29 | 30 | enum class Color(val rgb: Int) { // 1 31 | RED(0xFF0000), // 2 32 | GREEN(0x00FF00), 33 | BLUE(0x0000FF), 34 | YELLOW(0xFFFF00); 35 | 36 | fun containsRed() = (this.rgb and 0xFF0000 != 0) // 3 37 | } 38 | 39 | fun main() { 40 | val red = Color.RED 41 | println(red) // 4 42 | println(red.containsRed()) // 5 43 | println(Color.BLUE.containsRed()) // 6 44 | println(Color.YELLOW.containsRed()) // 7 45 | } 46 | 47 | ``` 48 | 49 | 1. Defines an enum class with a property and a method. 50 | 2. Each enum constant must pass an argument for the constructor parameter. 51 | 3. Enum class members are separated from the constant definitions by a semicolon. 52 | 4. The default `toString` returns the name of the constant, here `"RED"`. 53 | 5. Calls a method on an enum constant. 54 | 6. Calls a method via enum class name. 55 | 7. The RGB values of `RED` and `YELLOW` share first bits (`FF`) so this prints 'true'. 56 | -------------------------------------------------------------------------------- /examples/03_special_classes/03_Sealed Classes.md: -------------------------------------------------------------------------------- 1 | # Sealed Classes 2 | 3 | [Sealed classes](https://kotlinlang.org/docs/reference/sealed-classes.html) let you restrict the use of inheritance. Once you declare a class sealed, it can only be subclassed from inside the same package where the sealed class is declared. It cannot be subclassed outside of the package where the sealed class is declared. 4 | 5 | ```run-kotlin 6 | sealed class Mammal(val name: String) // 1 7 | 8 | class Cat(val catName: String) : Mammal(catName) // 2 9 | class Human(val humanName: String, val job: String) : Mammal(humanName) 10 | 11 | fun greetMammal(mammal: Mammal): String { 12 | when (mammal) { // 3 13 | is Human -> return "Hello ${mammal.name}; You're working as a ${mammal.job}" // 4 14 | is Cat -> return "Hello ${mammal.name}" // 5 15 | } // 6 16 | } 17 | 18 | fun main() { 19 | println(greetMammal(Cat("Snowy"))) 20 | } 21 | ``` 22 | 23 | 1. Defines a sealed class. 24 | 2. Defines subclasses. Note that all subclasses must be in the same package. 25 | 3. Uses an instance of the sealed class as an argument in a `when` expression. 26 | 4. A smartcast is performed, casting `Mammal` to `Human`. 27 | 5. A smartcast is performed, casting `Mammal` to `Cat`. 28 | 6. The `else`-case is not necessary here since all possible subclasses of the sealed class are covered. With a non-sealed superclass `else` would be required. 29 | 30 | 31 | -------------------------------------------------------------------------------- /examples/03_special_classes/04_Object.md: -------------------------------------------------------------------------------- 1 | # Object Keyword 2 | 3 | Classes and objects in Kotlin work the same way as in most object-oriented languages: a *class* is a blueprint, and an *object* is an instance of a class. Usually, you define a class and then create multiple instances of that class: 4 | 5 | ```run-kotlin 6 | import java.util.Random 7 | 8 | class LuckDispatcher { //1 9 | fun getNumber() { //2 10 | var objRandom = Random() 11 | println(objRandom.nextInt(90)) 12 | } 13 | } 14 | 15 | fun main() { 16 | val d1 = LuckDispatcher() //3 17 | val d2 = LuckDispatcher() 18 | 19 | d1.getNumber() //4 20 | d2.getNumber() 21 | } 22 | ``` 23 | 24 | 1. Defines a blueprint. 25 | 2. Defines a method. 26 | 3. Creates instances. 27 | 4. Calls the method on instances. 28 | 29 | In Kotlin you also have the [**object** keyword](https://kotlinlang.org/docs/reference/object-declarations.html). It is used to obtain a *data type with a single implementation*. 30 | 31 | If you are a Java user and want to understand what "*single*" means, you can think of the **Singleton** pattern: 32 | it ensures you that only one instance of that class is created even if 2 threads try to create it. 33 | 34 | To achieve this in Kotlin, you only need to declare an `object`: no class, no constructor, only a lazy instance. 35 | Why lazy? Because it will be created once when the object is accessed. Otherwise, it won't even be created. 36 | 37 | ### `object` Expression 38 | 39 | Here is a basic typical usage of an `object` **expression**: a simple object/properties structure. 40 | There is no need to do so in class declaration: you create a single object, declare its members and access it within one function. 41 | Objects like this are often created in Java as anonymous class instances. 42 | 43 | ```run-kotlin 44 | fun rentPrice(standardDays: Int, festivityDays: Int, specialDays: Int): Unit { //1 45 | 46 | val dayRates = object { //2 47 | var standard: Int = 30 * standardDays 48 | var festivity: Int = 50 * festivityDays 49 | var special: Int = 100 * specialDays 50 | } 51 | 52 | val total = dayRates.standard + dayRates.festivity + dayRates.special //3 53 | 54 | print("Total price: $$total") //4 55 | 56 | } 57 | 58 | fun main() { 59 | rentPrice(10, 2, 1) //5 60 | } 61 | ``` 62 | 63 | 1. Creates a function with parameters. 64 | 2. Creates an object to use when calculating the result value. 65 | 3. Accesses the object's properties. 66 | 4. Prints the result. 67 | 5. Calls the function. This is when the object is actually created. 68 | 69 | ### `object` Declaration 70 | 71 | You can also use the `object` **declaration**. It isn't an expression, and can't be used in a variable assignment. You should use it to directly access its members: 72 | 73 | ```run-kotlin 74 | object DoAuth { //1 75 | fun takeParams(username: String, password: String) { //2 76 | println("input Auth parameters = $username:$password") 77 | } 78 | } 79 | 80 | fun main(){ 81 | DoAuth.takeParams("foo", "qwerty") //3 82 | } 83 | 84 | ``` 85 | 86 | 1. Creates an object declaration. 87 | 2. Defines the object method. 88 | 3. Calls the method. This is when the object is actually created. 89 | 90 | ### Companion Objects 91 | 92 | An object declaration inside a class defines another useful case: the **companion object**. 93 | Syntactically it's similar to the static methods in Java: you call object members using its *class name* as a qualifier. 94 | If you plan to use a companion object in Kotlin, consider using a *package-level* function instead. 95 | 96 | ```run-kotlin 97 | class BigBen { //1 98 | companion object Bonger { //2 99 | fun getBongs(nTimes: Int) { //3 100 | for (i in 1 .. nTimes) { 101 | print("BONG ") 102 | } 103 | } 104 | } 105 | } 106 | 107 | fun main() { 108 | BigBen.getBongs(12) //4 109 | } 110 | ``` 111 | 112 | 1. Defines a class. 113 | 2. Defines a companion. Its name can be omitted. 114 | 3. Defines a companion object method. 115 | 4. Calls the companion object method via the class name. 116 | -------------------------------------------------------------------------------- /examples/04_functional/00_description.md: -------------------------------------------------------------------------------- 1 | # Functional 2 | -------------------------------------------------------------------------------- /examples/04_functional/01_Higher-Order Functions.md: -------------------------------------------------------------------------------- 1 | # Higher-Order Functions 2 | 3 | A [*higher-order function*](https://kotlinlang.org/docs/reference/lambdas.html) is a function that takes another function as parameter and/or returns a function. 4 | 5 | ### Taking Functions as Parameters 6 | 7 | ```run-kotlin 8 | fun calculate(x: Int, y: Int, operation: (Int, Int) -> Int): Int { // 1 9 | return operation(x, y) // 2 10 | } 11 | 12 | fun sum(x: Int, y: Int) = x + y // 3 13 | 14 | fun main() { 15 | val sumResult = calculate(4, 5, ::sum) // 4 16 | val mulResult = calculate(4, 5) { a, b -> a * b } // 5 17 | println("sumResult $sumResult, mulResult $mulResult") 18 | } 19 | ``` 20 | 21 | 1. Declares a higher-order function. It takes two integer parameters, `x` and `y`. Additionally, it takes another function `operation` as a parameter. The `operation` parameters and return type are also defined in the declaration. 22 | 2. The higher order function returns the result of `operation` invocation with the supplied arguments. 23 | 3. Declares a function that matches the `operation`signature. 24 | 4. Invokes the higher-order function passing in two integer values and the function argument `::sum`. `::` is the notation that references a function by name in Kotlin. 25 | 5. Invokes the higher-order function passing in a lambda as a function argument. Looks clearer, doesn't it? 26 | 27 | ### Returning Functions 28 | 29 | ```run-kotlin 30 | fun operation(): (Int) -> Int { // 1 31 | return ::square 32 | } 33 | 34 | fun square(x: Int) = x * x // 2 35 | 36 | fun main() { 37 | val func = operation() // 3 38 | println(func(2)) // 4 39 | } 40 | ``` 41 | 42 | 1. Declares a higher-order function that returns a function. `(Int) -> Int` represents the parameters and return type of the `square` function. 43 | 2. Declares a function matching the signature. 44 | 3. Invokes `operation` to get the result assigned to a variable. Here `func` becomes `square` which is returned by `operation`. 45 | 4. Invokes `func`. The `square` function is actually executed. 46 | -------------------------------------------------------------------------------- /examples/04_functional/02_Lambdas.md: -------------------------------------------------------------------------------- 1 | # Lambda Functions 2 | 3 | [*Lambda functions*](https://kotlinlang.org/docs/reference/lambdas.html) ("lambdas") are a simple way to create functions ad-hoc. Lambdas can be denoted very concisely in many cases thanks to type inference and the implicit `it` variable. 4 | 5 | ```run-kotlin 6 | fun main() { 7 | //sampleStart 8 | // All examples create a function object that performs upper-casing. 9 | // So it's a function from String to String 10 | 11 | val upperCase1: (String) -> String = { str: String -> str.uppercase() } // 1 12 | 13 | val upperCase2: (String) -> String = { str -> str.uppercase() } // 2 14 | 15 | val upperCase3 = { str: String -> str.uppercase() } // 3 16 | 17 | // val upperCase4 = { str -> str.uppercase() } // 4 18 | 19 | val upperCase5: (String) -> String = { it.uppercase() } // 5 20 | 21 | val upperCase6: (String) -> String = String::uppercase // 6 22 | 23 | println(upperCase1("hello")) 24 | println(upperCase2("hello")) 25 | println(upperCase3("hello")) 26 | println(upperCase5("hello")) 27 | println(upperCase6("hello")) 28 | //sampleEnd 29 | } 30 | ``` 31 | 32 | 1. A lambda in all its glory, with explicit types everywhere. The lambda is the part in curly braces, which is assigned to a variable of type `(String) -> String` (a function type). 33 | 2. Type inference inside lambda: the type of the lambda parameter is inferred from the type of the variable it's assigned to. 34 | 3. Type inference outside lambda: the type of the variable is inferred from the type of the lambda parameter and return value. 35 | 4. You cannot do both together, the compiler has no chance to infer the type that way. 36 | 5. For lambdas with a single parameter, you don't have to explicitly name it. Instead, you can use the implicit `it` variable. This is especially useful when the type of `it` can be inferred (which is often the case). 37 | 6. If your lambda consists of a single function call, you may use function pointers (`::`) . 38 | -------------------------------------------------------------------------------- /examples/04_functional/03_extensionFunctions.md: -------------------------------------------------------------------------------- 1 | # Extension Functions and Properties 2 | 3 | Kotlin lets you add new members to any class with the [extensions](https://kotlinlang.org/docs/reference/extensions.html) mechanism. Namely, there are two types of extensions: extension functions and extension properties. They look a lot like normal functions and properties but with one important difference: you need to specify the type that you extend. 4 | 5 | ```run-kotlin 6 | data class Item(val name: String, val price: Float) // 1 7 | 8 | data class Order(val items: Collection) 9 | 10 | fun Order.maxPricedItemValue(): Float = this.items.maxByOrNull { it.price }?.price ?: 0F // 2 11 | fun Order.maxPricedItemName() = this.items.maxByOrNull { it.price }?.name ?: "NO_PRODUCTS" 12 | 13 | val Order.commaDelimitedItemNames: String // 3 14 | get() = items.map { it.name }.joinToString() 15 | 16 | fun main() { 17 | 18 | val order = Order(listOf(Item("Bread", 25.0F), Item("Wine", 29.0F), Item("Water", 12.0F))) 19 | 20 | println("Max priced item name: ${order.maxPricedItemName()}") // 4 21 | println("Max priced item value: ${order.maxPricedItemValue()}") 22 | println("Items: ${order.commaDelimitedItemNames}") // 5 23 | 24 | } 25 | ``` 26 | 27 | 1. Defines simple models of `Item` and `Order`. `Order` can contain a collection of `Item` objects. 28 | 2. Adds extension functions for the `Order` type. 29 | 3. Adds an extension property for the `Order` type. 30 | 4. Calls extension functions directly on an instance of `Order`. 31 | 5. Accesses the extension property on an instance of `Order`. 32 | 33 | It is even possible to execute extensions on `null` references. In an extension function, you can check the object for `null` and use the result in your code: 34 | 35 | ```run-kotlin 36 | //sampleStart 37 | fun T?.nullSafeToString() = this?.toString() ?: "NULL" // 1 38 | //sampleEnd 39 | fun main() { 40 | println(null.nullSafeToString()) 41 | println("Kotlin".nullSafeToString()) 42 | } 43 | ``` 44 | -------------------------------------------------------------------------------- /examples/05_Collections/00_description.md: -------------------------------------------------------------------------------- 1 | # Collections -------------------------------------------------------------------------------- /examples/05_Collections/01_List.md: -------------------------------------------------------------------------------- 1 | # List 2 | 3 | A [list](https://kotlinlang.org/docs/reference/collections.html) is an ordered collection of items. In Kotlin, lists can be either mutable (`MutableList`) or read-only (`List`). For list creation, use the standard library functions `listOf()` for read-only lists and `mutableListOf()` for mutable lists. To prevent unwanted modifications, obtain read-only views of mutable lists by casting them to `List`. 4 | 5 | ```run-kotlin 6 | val systemUsers: MutableList = mutableListOf(1, 2, 3) // 1 7 | val sudoers: List = systemUsers // 2 8 | 9 | fun addSystemUser(newUser: Int) { // 3 10 | systemUsers.add(newUser) 11 | } 12 | 13 | fun getSysSudoers(): List { // 4 14 | return sudoers 15 | } 16 | 17 | fun main() { 18 | addSystemUser(4) // 5 19 | println("Tot sudoers: ${getSysSudoers().size}") // 6 20 | getSysSudoers().forEach { // 7 21 | i -> println("Some useful info on user $i") 22 | } 23 | // getSysSudoers().add(5) <- Error! // 8 24 | } 25 | ``` 26 | 27 | 1. Creates a `MutableList`. 28 | 2. Creates a read-only view of the list. 29 | 3. Adds a new item to the `MutableList`. 30 | 4. A function that returns an immutable `List`. 31 | 5. Updates the `MutableList`. All related read-only views are updated as well since they point to the same object. 32 | 6. Retrieves the size of the read-only list. 33 | 7. Iterates the list and prints its elements. 34 | 8. Attempting to write to the read-only view causes a compilation error. 35 | -------------------------------------------------------------------------------- /examples/05_Collections/02_Set.md: -------------------------------------------------------------------------------- 1 | # Set 2 | 3 | A [set](https://kotlinlang.org/docs/reference/collections.html) is an unordered collection that does not support duplicates. For creating sets, there are functions `setOf()` and `mutableSetOf()`. A read-only view of a mutable set can be obtained by casting it to `Set`. 4 | 5 | ```run-kotlin 6 | val openIssues: MutableSet = mutableSetOf("uniqueDescr1", "uniqueDescr2", "uniqueDescr3") // 1 7 | 8 | fun addIssue(uniqueDesc: String): Boolean { 9 | return openIssues.add(uniqueDesc) // 2 10 | } 11 | 12 | fun getStatusLog(isAdded: Boolean): String { 13 | return if (isAdded) "registered correctly." else "marked as duplicate and rejected." // 3 14 | } 15 | 16 | fun main() { 17 | val aNewIssue: String = "uniqueDescr4" 18 | val anIssueAlreadyIn: String = "uniqueDescr2" 19 | 20 | println("Issue $aNewIssue ${getStatusLog(addIssue(aNewIssue))}") // 4 21 | println("Issue $anIssueAlreadyIn ${getStatusLog(addIssue(anIssueAlreadyIn))}") // 5 22 | } 23 | ``` 24 | 25 | 1. Creates a `Set` with given elements. 26 | 2. Returns a boolean value showing if the element was actually added. 27 | 3. Returns a string, based on function input parameter. 28 | 4. Prints a success message: the new element is added to the `Set`. 29 | 4. Prints a failure message: the element can't be added because it duplicates an existing element. 30 | 31 | -------------------------------------------------------------------------------- /examples/05_Collections/03_Map.md: -------------------------------------------------------------------------------- 1 | # Map 2 | 3 | A [map](https://kotlinlang.org/docs/reference/collections.html) is a collection of key/value pairs, where each key is unique and is used to retrieve the corresponding value. For creating maps, there are functions `mapOf()` and `mutableMapOf()`. Using the [to](https://kotlinlang.org/api/latest/jvm/stdlib/kotlin/to.html) infix function makes initialization less noisy. A read-only view of a mutable map can be obtained by casting it to `Map`. 4 | 5 | ```run-kotlin 6 | const val POINTS_X_PASS: Int = 15 7 | val EZPassAccounts: MutableMap = mutableMapOf(1 to 100, 2 to 100, 3 to 100) // 1 8 | val EZPassReport: Map = EZPassAccounts // 2 9 | 10 | fun updatePointsCredit(accountId: Int) { 11 | if (EZPassAccounts.containsKey(accountId)) { // 3 12 | println("Updating $accountId...") 13 | EZPassAccounts[accountId] = EZPassAccounts.getValue(accountId) + POINTS_X_PASS // 4 14 | } else { 15 | println("Error: Trying to update a non-existing account (id: $accountId)") 16 | } 17 | } 18 | 19 | fun accountsReport() { 20 | println("EZ-Pass report:") 21 | EZPassReport.forEach { // 5 22 | k, v -> println("ID $k: credit $v") 23 | } 24 | } 25 | 26 | fun main() { 27 | accountsReport() // 6 28 | updatePointsCredit(1) // 7 29 | updatePointsCredit(1) 30 | updatePointsCredit(5) // 8 31 | accountsReport() // 9 32 | } 33 | ``` 34 | 35 | 1. Creates a mutable `Map`. 36 | 2. Creates a read-only view of the `Map`. 37 | 3. Checks if the `Map`'s key exists. 38 | 4. Reads the corresponding value and increments it with a constant value. 39 | 5. Iterates immutable `Map` and prints key/value pairs. 40 | 6. Reads the account points balance, before updates. 41 | 7. Updates an existing account two times. 42 | 8. Tries to update a non-existing account: prints an error message. 43 | 9. Reads the account points balance, after updates. 44 | -------------------------------------------------------------------------------- /examples/05_Collections/04_filter.md: -------------------------------------------------------------------------------- 1 | # filter 2 | 3 | *filter* function enables you to filter collections. It takes a filter predicate as a lambda-parameter. The predicate is applied to each element. Elements that make the predicate `true` are returned in the result collection. 4 | 5 | ```run-kotlin 6 | fun main() { 7 | 8 | //sampleStart 9 | val numbers = listOf(1, -2, 3, -4, 5, -6) // 1 10 | 11 | val positives = numbers.filter { x -> x > 0 } // 2 12 | 13 | val negatives = numbers.filter { it < 0 } // 3 14 | //sampleEnd 15 | 16 | println("Numbers: $numbers") 17 | println("Positive Numbers: $positives") 18 | println("Negative Numbers: $negatives") 19 | } 20 | ``` 21 | 22 | 1. Defines collection of numbers. 23 | 2. Gets positive numbers. 24 | 3. Uses the shorter `it` notation to get negative numbers. 25 | -------------------------------------------------------------------------------- /examples/05_Collections/04_map.md: -------------------------------------------------------------------------------- 1 | # map 2 | 3 | *map* extension function enables you to apply a transformation to all elements in a collection. It takes a transformer function as a lambda-parameter. 4 | 5 | ```run-kotlin 6 | fun main() { 7 | 8 | //sampleStart 9 | val numbers = listOf(1, -2, 3, -4, 5, -6) // 1 10 | 11 | val doubled = numbers.map { x -> x * 2 } // 2 12 | 13 | val tripled = numbers.map { it * 3 } // 3 14 | //sampleEnd 15 | 16 | println("Numbers: $numbers") 17 | println("Doubled Numbers: $doubled") 18 | println("Tripled Numbers: $tripled") 19 | } 20 | ``` 21 | 22 | 1. Defines a collection of numbers. 23 | 2. Doubles numbers. 24 | 3. Uses the shorter `it` notation to triple the numbers. 25 | -------------------------------------------------------------------------------- /examples/05_Collections/05_existential.md: -------------------------------------------------------------------------------- 1 | # any, all, none 2 | 3 | These functions check the existence of collection elements that match a given predicate. 4 | 5 | ### Function `any` 6 | 7 | Function `any` returns `true` if the collection contains **at least one** element that matches the given predicate. 8 | 9 | ```run-kotlin 10 | fun main() { 11 | 12 | //sampleStart 13 | val numbers = listOf(1, -2, 3, -4, 5, -6) // 1 14 | 15 | val anyNegative = numbers.any { it < 0 } // 2 16 | 17 | val anyGT6 = numbers.any { it > 6 } // 3 18 | //sampleEnd 19 | 20 | println("Numbers: $numbers") 21 | println("Is there any number less than 0: $anyNegative") 22 | println("Is there any number greater than 6: $anyGT6") 23 | } 24 | ``` 25 | 26 | 1. Defines a collection of numbers. 27 | 2. Checks if there are negative elements. 28 | 3. Checks if there are elements greater than `6`. 29 | 30 | 31 | ### Function `all` 32 | 33 | Function `all` returns `true` if **all** elements in collection match the given predicate. 34 | 35 | ```run-kotlin 36 | fun main() { 37 | 38 | //sampleStart 39 | val numbers = listOf(1, -2, 3, -4, 5, -6) // 1 40 | 41 | val allEven = numbers.all { it % 2 == 0 } // 2 42 | 43 | val allLess6 = numbers.all { it < 6 } // 3 44 | //sampleEnd 45 | 46 | println("Numbers: $numbers") 47 | println("All numbers are even: $allEven") 48 | println("All numbers are less than 6: $allLess6") 49 | } 50 | ``` 51 | 52 | 1. Defines a collection of numbers. 53 | 2. Checks whether all elements are even. 54 | 3. Checks whether all elements are less than `6`. 55 | 56 | 57 | ### Function `none` 58 | 59 | Function `none` returns `true` if there are **no** elements that match the given predicate in the collection. 60 | 61 | ```run-kotlin 62 | fun main() { 63 | 64 | //sampleStart 65 | val numbers = listOf(1, -2, 3, -4, 5, -6) // 1 66 | 67 | val allEven = numbers.none { it % 2 == 1 } // 2 68 | 69 | val allLess6 = numbers.none { it > 6 } // 3 70 | //sampleEnd 71 | 72 | println("Numbers: $numbers") 73 | println("All numbers are even: $allEven") 74 | println("No element greater than 6: $allLess6") 75 | } 76 | ``` 77 | 78 | 1. Defines a collection of numbers. 79 | 2. Checks if there are no odd elements (all elements are even). 80 | 3. Checks if there are no elements greater than 6. 81 | 82 | -------------------------------------------------------------------------------- /examples/05_Collections/06_find.md: -------------------------------------------------------------------------------- 1 | # find, findLast 2 | 3 | The `find` and `findLast` functions return the first or the last collection element that matches the given predicate. If there are no such elements, these functions return `null`. 4 | 5 | 6 | ```run-kotlin 7 | fun main() { 8 | 9 | //sampleStart 10 | val words = listOf("Lets", "find", "something", "in", "collection", "somehow") // 1 11 | 12 | val first = words.find { it.startsWith("some") } // 2 13 | val last = words.findLast { it.startsWith("some") } // 3 14 | 15 | val nothing = words.find { it.contains("nothing") } // 4 16 | //sampleEnd 17 | 18 | println("The first word starting with \"some\" is \"$first\"") 19 | println("The last word starting with \"some\" is \"$last\"") 20 | println("The first word containing \"nothing\" is ${nothing?.let { "\"$it\"" } ?: "null"}") 21 | } 22 | ``` 23 | 24 | 1. Defines a collection of words. 25 | 2. Looks for the first word starting with "some". 26 | 3. Looks for the last word starting with "some". 27 | 4. Looks for the first word containing "nothing". 28 | -------------------------------------------------------------------------------- /examples/05_Collections/07_firstlast.md: -------------------------------------------------------------------------------- 1 | # first, last 2 | 3 | ### `first`, `last` 4 | 5 | These functions return the first and the last element of the collection correspondingly. You can also use them with a predicate; in this case, they return the first or the last element that matches the given predicate. 6 | 7 | If a collection is empty or doesn't contain elements matching the predicate, the functions throw `NoSuchElementException`. 8 | 9 | ```run-kotlin 10 | fun main() { 11 | 12 | //sampleStart 13 | val numbers = listOf(1, -2, 3, -4, 5, -6) // 1 14 | 15 | val first = numbers.first() // 2 16 | val last = numbers.last() // 3 17 | 18 | val firstEven = numbers.first { it % 2 == 0 } // 4 19 | val lastOdd = numbers.last { it % 2 != 0 } // 5 20 | //sampleEnd 21 | 22 | println("Numbers: $numbers") 23 | println("First $first, last $last, first even $firstEven, last odd $lastOdd") 24 | } 25 | ``` 26 | 27 | 1. Defines a collection of numbers. 28 | 2. Picks the first element. 29 | 3. Picks the last element. 30 | 4. Picks the first even element. 31 | 5. Picks the last odd element. 32 | 33 | ### `firstOrNull`, `lastOrNull` 34 | 35 | These functions work almost the same way with one difference: they return `null` if there are no matching elements. 36 | 37 | ```run-kotlin 38 | fun main() { 39 | 40 | //sampleStart 41 | val words = listOf("foo", "bar", "baz", "faz") // 1 42 | val empty = emptyList() // 2 43 | 44 | val first = empty.firstOrNull() // 3 45 | val last = empty.lastOrNull() // 4 46 | 47 | val firstF = words.firstOrNull { it.startsWith('f') } // 5 48 | val firstZ = words.firstOrNull { it.startsWith('z') } // 6 49 | val lastF = words.lastOrNull { it.endsWith('f') } // 7 50 | val lastZ = words.lastOrNull { it.endsWith('z') } // 8 51 | //sampleEnd 52 | 53 | println("Empty list: first is $first, last is $last") 54 | println("Word list: first item starting with 'f' is $firstF, first item starting with 'z' is $firstZ") 55 | println("Word list: last item ending with 'f' is $lastF, last item ending with 'z' is $lastZ") 56 | } 57 | ``` 58 | 59 | 1. Defines a collection of words. 60 | 2. Defines an empty collection. 61 | 3. Picks the first element from empty collection. It supposed to be `null`. 62 | 4. Picks the last element from empty collection. It supposed to be `null` as well. 63 | 5. Picks the first word starting with 'f'. 64 | 6. Picks the first word starting with 'z'. 65 | 7. Picks the last word ending with 'f'. 66 | 8. Picks the last word ending with 'z'. 67 | -------------------------------------------------------------------------------- /examples/05_Collections/08_count.md: -------------------------------------------------------------------------------- 1 | # count 2 | 3 | `count` functions returns either the total number of elements in a collection or the number of elements matching the given predicate. 4 | 5 | ```run-kotlin 6 | fun main() { 7 | 8 | //sampleStart 9 | val numbers = listOf(1, -2, 3, -4, 5, -6) // 1 10 | 11 | val totalCount = numbers.count() // 2 12 | val evenCount = numbers.count { it % 2 == 0 } // 3 13 | //sampleEnd 14 | 15 | println("Total number of elements: $totalCount") 16 | println("Number of even elements: $evenCount") 17 | } 18 | ``` 19 | 20 | 1. Defines a collection of numbers. 21 | 2. Counts the total number of elements. 22 | 3. Counts the number of even elements. 23 | -------------------------------------------------------------------------------- /examples/05_Collections/10_associateBy.md: -------------------------------------------------------------------------------- 1 | # associateBy, groupBy 2 | 3 | Functions `associateBy` and `groupBy` build maps from the elements of a collection indexed by the specified key. The key is defined in the `keySelector` parameter. 4 | You can also specify an optional `valueSelector` to define what will be stored in the `value` of the map element. 5 | 6 | The difference between `associateBy` and `groupBy` is how they process objects with the same key: 7 | 8 | * `associateBy` uses the last suitable element as the value. 9 | * `groupBy` builds a list of all suitable elements and puts it in the value. 10 | 11 | The returned map preserves the entry iteration order of the original collection. 12 | 13 | ```run-kotlin 14 | fun main() { 15 | 16 | //sampleStart 17 | 18 | data class Person(val name: String, val city: String, val phone: String) // 1 19 | 20 | val people = listOf( // 2 21 | Person("John", "Boston", "+1-888-123456"), 22 | Person("Sarah", "Munich", "+49-777-789123"), 23 | Person("Svyatoslav", "Saint-Petersburg", "+7-999-456789"), 24 | Person("Vasilisa", "Saint-Petersburg", "+7-999-123456")) 25 | 26 | val phoneBook = people.associateBy { it.phone } // 3 27 | val cityBook = people.associateBy(Person::phone, Person::city) // 4 28 | val peopleCities = people.groupBy(Person::city, Person::name) // 5 29 | val lastPersonCity = people.associateBy(Person::city, Person::name) // 6 30 | 31 | //sampleEnd 32 | 33 | println("People: $people") 34 | println("Phone book: $phoneBook") 35 | println("City book: $cityBook") 36 | println("People living in each city: $peopleCities") 37 | println("Last person living in each city: $lastPersonCity") 38 | } 39 | ``` 40 | 41 | 1. Defines a data class that describes a person. 42 | 2. Defines a collection of people. 43 | 3. Builds a map from phone numbers to their owners' information. `it.phone` is the `keySelector` here. The `valueSelector` is not provided, so the values of the map are `Person` objects themselves. 44 | 4. Builds a map from phone numbers to the cities where owners live. `Person::city` is the `valueSelector` here, so the values of the map contain only cities. 45 | 5. Builds a map that contains cities and people living there. The values of the map are lists of person names. 46 | 6. Builds a map that contains cities and the last person living there. The values of the map are names of the last person living in each city. 47 | -------------------------------------------------------------------------------- /examples/05_Collections/11_partition.md: -------------------------------------------------------------------------------- 1 | # partition 2 | 3 | The `partition` function splits the original collection into a pair of lists using a given predicate: 4 | 5 | 1. Elements for which the predicate is `true`. 6 | 1. Elements for which the predicate is `false`. 7 | 8 | ```run-kotlin 9 | fun main() { 10 | 11 | //sampleStart 12 | val numbers = listOf(1, -2, 3, -4, 5, -6) // 1 13 | 14 | val evenOdd = numbers.partition { it % 2 == 0 } // 2 15 | val (positives, negatives) = numbers.partition { it > 0 } // 3 16 | //sampleEnd 17 | 18 | println("Numbers: $numbers") 19 | println("Even numbers: ${evenOdd.first}") 20 | println("Odd numbers: ${evenOdd.second}") 21 | println("Positive numbers: $positives") 22 | println("Negative numbers: $negatives") 23 | } 24 | 25 | ``` 26 | 27 | 1. Defines a collection of numbers. 28 | 2. Splits `numbers` into a `Pair` of lists with even and odd numbers. 29 | 3. Splits `numbers` into two lists with positive and negative numbers. Pair destructuring is applied here to get the `Pair` members. 30 | -------------------------------------------------------------------------------- /examples/05_Collections/12_flatMap.md: -------------------------------------------------------------------------------- 1 | # flatMap 2 | 3 | `flatMap` transforms each element of a collection into an iterable object and builds a single list of the transformation results. The transformation is user-defined. 4 | 5 | ```run-kotlin 6 | fun main() { 7 | 8 | //sampleStart 9 | val fruitsBag = listOf("apple","orange","banana","grapes") // 1 10 | val clothesBag = listOf("shirts","pants","jeans") // 2 11 | val cart = listOf(fruitsBag, clothesBag) // 3 12 | val mapBag = cart.map { it } // 4 13 | val flatMapBag = cart.flatMap { it } // 5 14 | //sampleEnd 15 | 16 | println("Your bags are: $mapBag") 17 | println("The things you bought are: $flatMapBag") 18 | } 19 | ``` 20 | 21 | 1. Defines a collection of Strings with fruit names. 22 | 2. Defines a collection of Strings with clothes names. 23 | 3. Adds `fruitsBag` and `clothesBag` to the `cart` list. 24 | 4. Builds a `map` of `cart` elements, which is a list of two lists. 25 | 5. Builds a `flatMap` of `cart` elements, which is a single list consisting of elements from both lists. -------------------------------------------------------------------------------- /examples/05_Collections/13_max.md: -------------------------------------------------------------------------------- 1 | # minOrNull, maxOrNull 2 | 3 | `minOrNull` and `maxOrNull` functions return the smallest and the largest element of a collection. If the collection is empty, they return `null`. 4 | 5 | ```run-kotlin 6 | fun main() { 7 | 8 | //sampleStart 9 | val numbers = listOf(1, 2, 3) 10 | val empty = emptyList() 11 | val only = listOf(3) 12 | 13 | println("Numbers: $numbers, min = ${numbers.minOrNull()} max = ${numbers.maxOrNull()}") // 1 14 | println("Empty: $empty, min = ${empty.minOrNull()}, max = ${empty.maxOrNull()}") // 2 15 | println("Only: $only, min = ${only.minOrNull()}, max = ${only.maxOrNull()}") // 3 16 | //sampleEnd 17 | } 18 | ``` 19 | 20 | 1. For non-empty collection, functions return the smallest and the largest element. 21 | 2. For empty collections, both functions return `null`. 22 | 3. For collection with only one element, both functions return the same value. 23 | -------------------------------------------------------------------------------- /examples/05_Collections/14_sorted.md: -------------------------------------------------------------------------------- 1 | # sorted 2 | 3 | `sorted` returns a list of collection elements sorted according to their natural sort order (ascending). 4 | 5 | `sortedBy` sorts elements according to natural sort order of the values returned by specified selector function. 6 | 7 | ```run-kotlin 8 | import kotlin.math.abs 9 | 10 | fun main() { 11 | 12 | //sampleStart 13 | val shuffled = listOf(5, 4, 2, 1, 3, -10) // 1 14 | val natural = shuffled.sorted() // 2 15 | val inverted = shuffled.sortedBy { -it } // 3 16 | val descending = shuffled.sortedDescending() // 4 17 | val descendingBy = shuffled.sortedByDescending { abs(it) } // 5 18 | //sampleEnd 19 | 20 | println("Shuffled: $shuffled") 21 | println("Natural order: $natural") 22 | println("Inverted natural order: $inverted") 23 | println("Inverted natural order value: $descending") 24 | println("Inverted natural order of absolute values: $descendingBy") 25 | } 26 | ``` 27 | 28 | 1. Defines a collection of shuffled numbers. 29 | 2. Sorts it in the natural order. 30 | 3. Sorts it in the inverted natural order by using `-it` as a selector function. 31 | 4. Sorts it in the inverted natural order by using `sortedDescending`. 32 | 5. Sorts it in the inverted natural order of items' absolute values by using `abs(it)` as a selector function. 33 | -------------------------------------------------------------------------------- /examples/05_Collections/15_Map_getValue.md: -------------------------------------------------------------------------------- 1 | # Map Element Access 2 | 3 | When applied to a map, `[]` operator returns the value corresponding to the given key, or `null` if there is no such key in the map. 4 | 5 | `getValue` function returns an existing value corresponding to the given key or throws an exception if the key wasn't found. 6 | For maps created with `withDefault`, `getValue` returns the default value instead of throwing an exception. 7 | 8 | ```run-kotlin 9 | fun main(args: Array) { 10 | 11 | //sampleStart 12 | val map = mapOf("key" to 42) 13 | 14 | val value1 = map["key"] // 1 15 | val value2 = map["key2"] // 2 16 | 17 | val value3: Int = map.getValue("key") // 1 18 | 19 | val mapWithDefault = map.withDefault { k -> k.length } 20 | val value4 = mapWithDefault.getValue("key2") // 3 21 | 22 | try { 23 | map.getValue("anotherKey") // 4 24 | } catch (e: NoSuchElementException) { 25 | println("Message: $e") 26 | } 27 | 28 | //sampleEnd 29 | 30 | println("value1 is $value1") 31 | println("value2 is $value2") 32 | println("value3 is $value3") 33 | println("value4 is $value4") 34 | } 35 | ``` 36 | 37 | 1. Returns 42 because it's the value corresponding to the key `"key"`. 38 | 2. Returns `null` because `"key2"` is not in the map. 39 | 3. Returns the default value because `"key2"` is absent. For this key it's 4. 40 | 4. Throws `NoSuchElementException` because `"anotherKey"` is not in the map. 41 | -------------------------------------------------------------------------------- /examples/05_Collections/16_zip.md: -------------------------------------------------------------------------------- 1 | # zip 2 | 3 | `zip` function merges two given collections into a new collection. By default, the result collection contains `Pairs` of source collection elements with the same index. However, you can define your own structure of the result collection element. 4 | 5 | The size of the result collection equals to the minimum size of a source collection. 6 | 7 | ```run-kotlin 8 | fun main() { 9 | 10 | //sampleStart 11 | val A = listOf("a", "b", "c") // 1 12 | val B = listOf(1, 2, 3, 4) // 1 13 | 14 | val resultPairs = A zip B // 2 15 | val resultReduce = A.zip(B) { a, b -> "$a$b" } // 3 16 | //sampleEnd 17 | 18 | println("A to B: $resultPairs") 19 | println("\$A\$B: $resultReduce") 20 | } 21 | ``` 22 | 23 | 1. Defines two collections. 24 | 2. Merges them into a list of pairs. The infix notation is used here. 25 | 3. Merges them into a list of String values by the given transformation. 26 | -------------------------------------------------------------------------------- /examples/05_Collections/17_getOrElse.md: -------------------------------------------------------------------------------- 1 | # getOrElse 2 | 3 | `getOrElse` provides safe access to elements of a collection. It takes an index and a function that provides the default value 4 | in cases when the index is out of bound. 5 | 6 | ```run-kotlin 7 | fun main() { 8 | 9 | //sampleStart 10 | val list = listOf(0, 10, 20) 11 | println(list.getOrElse(1) { 42 }) // 1 12 | println(list.getOrElse(10) { 42 }) // 2 13 | //sampleEnd 14 | } 15 | ``` 16 | 17 | 1. Prints the element at the index `1`. 18 | 2. Prints `42` because the index `10` is out of bounds. 19 | 20 | `getOrElse` can also be applied to `Map` to get the value for the given key. 21 | 22 | ```run-kotlin 23 | fun main() { 24 | 25 | //sampleStart 26 | val map = mutableMapOf() 27 | println(map.getOrElse("x") { 1 }) // 1 28 | 29 | map["x"] = 3 30 | println(map.getOrElse("x") { 1 }) // 2 31 | 32 | map["x"] = null 33 | println(map.getOrElse("x") { 1 }) // 3 34 | //sampleEnd 35 | } 36 | ``` 37 | 38 | 1. Prints the default value because the key `"x"` is not in the map. 39 | 2. Prints `3`, the value for the key `"x"`. 40 | 3. Prints the default value because the value for the key `"x"` is not defined. 41 | -------------------------------------------------------------------------------- /examples/06_scope_functions/00_description.md: -------------------------------------------------------------------------------- 1 | # Scope Functions 2 | 3 | -------------------------------------------------------------------------------- /examples/06_scope_functions/01_let.md: -------------------------------------------------------------------------------- 1 | # let 2 | 3 | The Kotlin standard library function `let` can be used for scoping and null-checks. When called on an object, `let` executes the given block of code and returns the result of its last expression. 4 | The object is accessible inside the block by the reference `it` (by default) or a custom name. 5 | 6 | ```run-kotlin 7 | fun customPrint(s: String) { 8 | print(s.uppercase()) 9 | } 10 | 11 | fun main() { 12 | //sampleStart 13 | val empty = "test".let { // 1 14 | customPrint(it) // 2 15 | it.isEmpty() // 3 16 | } 17 | println(" is empty: $empty") 18 | 19 | 20 | fun printNonNull(str: String?) { 21 | println("Printing \"$str\":") 22 | 23 | str?.let { // 4 24 | print("\t") 25 | customPrint(it) 26 | println() 27 | } 28 | } 29 | 30 | fun printIfBothNonNull(strOne: String?, strTwo: String?) { 31 | strOne?.let { firstString -> // 5 32 | strTwo?.let { secondString -> 33 | customPrint("$firstString : $secondString") 34 | println() 35 | } 36 | } 37 | } 38 | 39 | printNonNull(null) 40 | printNonNull("my string") 41 | printIfBothNonNull("First","Second") 42 | //sampleEnd 43 | } 44 | 45 | ``` 46 | 47 | 1. Calls the given block on the result on the string "_test_". 48 | 2. Calls the function on "_test_" by the `it` reference. 49 | 3. `let` returns the value of this expression. 50 | 4. Uses safe call, so `let` and its code block will be executed only on non-null values. 51 | 5. Uses the custom name instead of `it`, so that the nested `let` can access the context object of the outer `let`. 52 | -------------------------------------------------------------------------------- /examples/06_scope_functions/02_run.md: -------------------------------------------------------------------------------- 1 | # run 2 | 3 | Like [`let`](01_let), `run` is another scoping function from the standard library. Basically, it does the same: executes a code block and returns its result. 4 | The difference is that inside `run` the object is accessed by `this`. This is useful when you want to call the object's methods rather than pass it as an argument. 5 | 6 | ```run-kotlin 7 | 8 | fun main() { 9 | //sampleStart 10 | fun getNullableLength(ns: String?) { 11 | println("for \"$ns\":") 12 | ns?.run { // 1 13 | println("\tis empty? " + isEmpty()) // 2 14 | println("\tlength = $length") 15 | length // 3 16 | } 17 | } 18 | getNullableLength(null) 19 | getNullableLength("") 20 | getNullableLength("some string with Kotlin") 21 | //sampleEnd 22 | } 23 | ``` 24 | 25 | 1. Calls the given block on a nullable variable. 26 | 2. Inside `run`, the object's members are accessed without its name. 27 | 3. `run` returns the `length` of the given `String` if it's not `null`. 28 | -------------------------------------------------------------------------------- /examples/06_scope_functions/03_with.md: -------------------------------------------------------------------------------- 1 | 2 | 3 | # with 4 | 5 | `with` is a non-extension function that can access members of its argument concisely: you can omit the instance name when referring to its members. 6 | 7 | ```run-kotlin 8 | class Configuration(var host: String, var port: Int) 9 | 10 | fun main() { 11 | val configuration = Configuration(host = "127.0.0.1", port = 9000) 12 | 13 | //sampleStart 14 | with(configuration) { 15 | println("$host:$port") 16 | } 17 | 18 | // instead of: 19 | println("${configuration.host}:${configuration.port}") 20 | //sampleEnd 21 | } 22 | ``` 23 | -------------------------------------------------------------------------------- /examples/06_scope_functions/04_apply.md: -------------------------------------------------------------------------------- 1 | # apply 2 | 3 | `apply` executes a block of code on an object and returns the object itself. Inside the block, the object is referenced by `this`. 4 | This function is handy for initializing objects. 5 | 6 | ```run-kotlin 7 | data class Person(var name: String, var age: Int, var about: String) { 8 | constructor() : this("", 0, "") 9 | } 10 | 11 | fun main() { 12 | //sampleStart 13 | val jake = Person() // 1 14 | val stringDescription = jake.apply { // 2 15 | name = "Jake" // 3 16 | age = 30 17 | about = "Android developer" 18 | }.toString() // 4 19 | //sampleEnd 20 | println(stringDescription) 21 | } 22 | ``` 23 | 24 | 25 | 1. Creates a `Person()` instance with default property values. 26 | 2. Applies the code block (next 3 lines) to the instance. 27 | 3. Inside `apply`, it's equivalent to `jake.name = "Jake"`. 28 | 4. The return value is the instance itself, so you can chain other operations. 29 | -------------------------------------------------------------------------------- /examples/06_scope_functions/05_also.md: -------------------------------------------------------------------------------- 1 | # also 2 | 3 | `also` works like [`apply`](04_apply): it executes a given block and returns the object called. 4 | Inside the block, the object is referenced by `it`, so it's easier to pass it as an argument. 5 | This function is handy for embedding additional actions, such as logging in call chains. 6 | 7 | ```run-kotlin 8 | data class Person(var name: String, var age: Int, var about: String) { 9 | constructor() : this("", 0, "") 10 | } 11 | 12 | fun writeCreationLog(p: Person) { 13 | println("A new person ${p.name} was created.") 14 | } 15 | 16 | fun main() { 17 | //sampleStart 18 | val jake = Person("Jake", 30, "Android developer") // 1 19 | .also { // 2 20 | writeCreationLog(it) // 3 21 | } 22 | //sampleEnd 23 | } 24 | ``` 25 | 26 | 1. Creates a `Person()` object with the given property values. 27 | 2. Applies the given code block to the object. The return value is the object itself. 28 | 3. Calls the logging function passing the object as an argument. 29 | -------------------------------------------------------------------------------- /examples/07_Delegation/00_description.md: -------------------------------------------------------------------------------- 1 | # Delegation 2 | 3 | 4 | -------------------------------------------------------------------------------- /examples/07_Delegation/01_delegationPattern.md: -------------------------------------------------------------------------------- 1 | # Delegation Pattern 2 | 3 | Kotlin supports easy implementation of the [delegation pattern](https://kotlinlang.org/docs/reference/delegation.html) on the native level without any boilerplate code. 4 | 5 | ```run-kotlin 6 | interface SoundBehavior { // 1 7 | fun makeSound() 8 | } 9 | 10 | class ScreamBehavior(val n: String): SoundBehavior { // 2 11 | override fun makeSound() = println("${n.uppercase()} !!!") 12 | } 13 | 14 | class RockAndRollBehavior(val n: String): SoundBehavior { // 2 15 | override fun makeSound() = println("I'm The King of Rock 'N' Roll: $n") 16 | } 17 | 18 | // Tom Araya is the "singer" of Slayer 19 | class TomAraya(n: String): SoundBehavior by ScreamBehavior(n) // 3 20 | 21 | // You should know ;) 22 | class ElvisPresley(n: String): SoundBehavior by RockAndRollBehavior(n) // 3 23 | 24 | fun main() { 25 | val tomAraya = TomAraya("Thrash Metal") 26 | tomAraya.makeSound() // 4 27 | val elvisPresley = ElvisPresley("Dancin' to the Jailhouse Rock.") 28 | elvisPresley.makeSound() 29 | } 30 | ``` 31 | 32 | 33 | 1. Defines the interface `SoundBehavior` with one method. 34 | 2. The classes `ScreamBehavior` and `RockAndRollBehavior` implement the interface and contain their own implementations of the method. 35 | 3. The classes `TomAraya` and `ElvisPresley` also implement the interface, but not the method. Instead, they delegate the method calls to the 36 | responsible implementation. The delegate object is defined after the `by` keyword. As you see, no boilerplate code is required. 37 | 4. When `makeSound()` is called on `tomAraya` of type `TomAraya` or `elvisPresley` of type `ElvisPresley`, the call is delegated to the 38 | corresponding delegate object. 39 | -------------------------------------------------------------------------------- /examples/07_Delegation/02_DelegatedProperties.md: -------------------------------------------------------------------------------- 1 | # Delegated Properties 2 | 3 | Kotlin provides a mechanism of [delegated properties](http://kotlinlang.org/docs/reference/delegated-properties.html) that allows delegating the calls of the property `set` and `get` methods to a certain object. 4 | The delegate object in this case should have the method `getValue`. For mutable properties, you'll also need `setValue`. 5 | 6 | ```run-kotlin 7 | import kotlin.reflect.KProperty 8 | 9 | class Example { 10 | var p: String by Delegate() // 1 11 | 12 | override fun toString() = "Example Class" 13 | } 14 | 15 | class Delegate() { 16 | operator fun getValue(thisRef: Any?, prop: KProperty<*>): String { // 2 17 | return "$thisRef, thank you for delegating '${prop.name}' to me!" 18 | } 19 | 20 | operator fun setValue(thisRef: Any?, prop: KProperty<*>, value: String) { // 2 21 | println("$value has been assigned to ${prop.name} in $thisRef") 22 | } 23 | } 24 | 25 | fun main() { 26 | val e = Example() 27 | println(e.p) 28 | e.p = "NEW" 29 | } 30 | ``` 31 | 32 | 1. Delegates property `p` of type `String` to the instance of class `Delegate`. The delegate object is defined after the `by` keyword. 33 | 2. Delegation methods. The signatures of these methods are always as shown in the example. Implementations may contain any steps you need. For immutable properties only `getValue` is required. 34 | 35 | ### Standard Delegates 36 | 37 | The Kotlin standard library contains a bunch of useful delegates, like `lazy`, `observable`, and others. You may use them as is. 38 | For example `lazy`is used for lazy initialization. 39 | 40 | ```run-kotlin 41 | class LazySample { 42 | init { 43 | println("created!") // 1 44 | } 45 | 46 | val lazyStr: String by lazy { 47 | println("computed!") // 2 48 | "my lazy" 49 | } 50 | } 51 | 52 | fun main() { 53 | val sample = LazySample() // 1 54 | println("lazyStr = ${sample.lazyStr}") // 2 55 | println(" = ${sample.lazyStr}") // 3 56 | } 57 | ``` 58 | 59 | 1. Property `lazy` is not initialized on object creation. 60 | 2. The first call to `get()` executes the lambda expression passed to `lazy()` as an argument and saves the result. 61 | 3. Further calls to `get()` return the saved result. 62 | 63 | If you want thread safety, use `blockingLazy()` instead: it guarantees that the values will be computed only in one thread and that all threads will see the same value. 64 | 65 | ### Storing Properties in a Map 66 | 67 | Property delegation can be used for storing properties in a map. This is handy for tasks like parsing JSON 68 | or doing other "dynamic" stuff. 69 | 70 | ```run-kotlin 71 | class User(val map: Map) { 72 | val name: String by map // 1 73 | val age: Int by map // 1 74 | } 75 | 76 | fun main() { 77 | val user = User(mapOf( 78 | "name" to "John Doe", 79 | "age" to 25 80 | )) 81 | 82 | println("name = ${user.name}, age = ${user.age}") 83 | } 84 | ``` 85 | 86 | 1. Delegates take values from the `map` by the string keys - names of properties. 87 | 88 | You can delegate mutable properties to a map as well. In this case, the map will be modified upon property assignments. Note that you will need `MutableMap` instead of read-only `Map`. 89 | -------------------------------------------------------------------------------- /examples/08_productivity_boosters/00_description.md: -------------------------------------------------------------------------------- 1 | # Productivity Boosters 2 | 3 | -------------------------------------------------------------------------------- /examples/08_productivity_boosters/01_namedArguments.md: -------------------------------------------------------------------------------- 1 | # Named Arguments 2 | 3 | As with most other programming languages (Java, C++, etc.), Kotlin supports passing arguments to methods and constructors according to the order they are defined. 4 | Kotlin also supports [named arguments](https://kotlinlang.org/docs/reference/functions.html#named-arguments) to allow clearer invocations and avoid mistakes with the order of arguments. Such mistakes are hard to find because they are not detected by the compiler, for example, when two sequential arguments have the same type. 5 | 6 | ```run-kotlin 7 | fun format(userName: String, domain: String) = "$userName@$domain" 8 | 9 | fun main() { 10 | //sampleStart 11 | println(format("mario", "example.com")) // 1 12 | println(format("domain.com", "username")) // 2 13 | println(format(userName = "foo", domain = "bar.com")) // 3 14 | println(format(domain = "frog.com", userName = "pepe")) // 4 15 | //sampleEnd 16 | } 17 | ``` 18 | 19 | 1. Calls a function with argument values. 20 | 2. Calls a function with switched arguments. No syntax errors, but the result _domain.com@username_ is incorrect. 21 | 3. Calls a function with named arguments. 22 | 4. When invoking a function with named arguments, you can specify them in any order you like. 23 | -------------------------------------------------------------------------------- /examples/08_productivity_boosters/02_String Templates.md: -------------------------------------------------------------------------------- 1 | # String Templates 2 | 3 | [String templates](https://kotlinlang.org/docs/reference/basic-types.html#string-templates) allow you to include variable references and expressions into strings. When the value of a string is requested (for example, by `println`), all references and expressions are substituted with actual values. 4 | 5 | ```run-kotlin 6 | fun main() { 7 | //sampleStart 8 | val greeting = "Kotliner" 9 | 10 | println("Hello $greeting") // 1 11 | println("Hello ${greeting.uppercase()}") // 2 12 | //sampleEnd 13 | } 14 | ``` 15 | 16 | 1. Prints a string with a variable reference. References in strings start with `$`. 17 | 2. Prints a string with an expression. Expressions start with `$` and are enclosed in curly braces. 18 | 19 | -------------------------------------------------------------------------------- /examples/08_productivity_boosters/03_Destructuring Declarations.md: -------------------------------------------------------------------------------- 1 | # Destructuring Declarations 2 | 3 | [Destructuring declaration](https://kotlinlang.org/docs/reference/multi-declarations.html#destructuring-declarations) syntax can be very handy, especially when you need an instance only for accessing its members. It lets you define the instance without a specific name therefore saving a few lines of code. 4 | 5 | ```run-kotlin 6 | fun findMinMax(list: List): Pair { 7 | // do the math 8 | return Pair(50, 100) 9 | } 10 | 11 | fun main() { 12 | //sampleStart 13 | val (x, y, z) = arrayOf(5, 10, 15) // 1 14 | 15 | val map = mapOf("Alice" to 21, "Bob" to 25) 16 | for ((name, age) in map) { // 2 17 | println("$name is $age years old") 18 | } 19 | 20 | val (min, max) = findMinMax(listOf(100, 90, 50, 98, 76, 83)) // 3 21 | 22 | //sampleEnd 23 | } 24 | ``` 25 | 26 | 1. Destructures an `Array`. The number of variables on the left side matches the number of arguments on the right side. 27 | 2. Maps can be destructured as well. `name` and `age` variables are mapped to the map key and value. 28 | 3. Built-in `Pair` and `Triple` types support destructuring too, even as return values from functions. 29 | 30 | ```run-kotlin 31 | data class User(val username: String, val email: String) // 1 32 | 33 | fun getUser() = User("Mary", "mary@somewhere.com") 34 | 35 | fun main() { 36 | val user = getUser() 37 | val (username, email) = user // 2 38 | println(username == user.component1()) // 3 39 | 40 | val (_, emailAddress) = getUser() // 4 41 | 42 | } 43 | ``` 44 | 45 | 1. Defines a data class. 46 | 2. Destructures an instance. Declared values are mapped to the instance fields. 47 | 3. Data class automatically defines the `component1()` and `component2()` methods that will be called during destructuring. 48 | 4. Use _underscore_ if you don't need one of the values, avoiding the compiler hint indicating an unused variable. 49 | 50 | ```run-kotlin 51 | class Pair(val first: K, val second: V) { // 1 52 | operator fun component1(): K { 53 | return first 54 | } 55 | 56 | operator fun component2(): V { 57 | return second 58 | } 59 | } 60 | 61 | fun main() { 62 | val (num, name) = Pair(1, "one") // 2 63 | 64 | println("num = $num, name = $name") 65 | } 66 | ``` 67 | 68 | 1. Defines a custom `Pair` class with `component1()` and `component2()` methods. 69 | 2. Destructures an instance of this class the same way as for built-in `Pair`. 70 | -------------------------------------------------------------------------------- /examples/08_productivity_boosters/04_Smart Casts.md: -------------------------------------------------------------------------------- 1 | # Smart Casts 2 | 3 | 4 | The Kotlin compiler is smart enough to perform type casts automatically in most cases, including: 5 | 6 | 1. Casts from nullable types to their non-nullable counterparts. 7 | 2. Casts from a supertype to a subtype. 8 | 9 | ```run-kotlin 10 | import java.time.LocalDate 11 | import java.time.chrono.ChronoLocalDate 12 | 13 | fun main() { 14 | //sampleStart 15 | val date: ChronoLocalDate? = LocalDate.now() // 1 16 | 17 | if (date != null) { 18 | println(date.isLeapYear) // 2 19 | } 20 | 21 | if (date != null && date.isLeapYear) { // 3 22 | println("It's a leap year!") 23 | } 24 | 25 | if (date == null || !date.isLeapYear) { // 4 26 | println("There's no Feb 29 this year...") 27 | } 28 | 29 | if (date is LocalDate) { 30 | val month = date.monthValue // 5 31 | println(month) 32 | } 33 | //sampleEnd 34 | } 35 | 36 | ``` 37 | 38 | 1. Declares a nullable variable. 39 | 2. Smart-cast to non-nullable (thus allowing direct access to `isLeapYear`). 40 | 3. Smart-cast inside a condition (this is possible because, like Java, Kotlin uses [short-circuiting](https://en.wikipedia.org/wiki/Short-circuit_evaluation)). 41 | 4. Smart-cast inside a condition (also enabled by short-circuiting). 42 | 5. Smart-cast to the subtype `LocalDate`. 43 | 44 | This way, you can automatically use variables as desired in most cases without doing obvious casts manually. 45 | -------------------------------------------------------------------------------- /examples/09_Kotlin_JS/00_description.md: -------------------------------------------------------------------------------- 1 | # Kotlin/JS 2 | 3 | -------------------------------------------------------------------------------- /examples/09_Kotlin_JS/01_dynamic.md: -------------------------------------------------------------------------------- 1 | # dynamic 2 | 3 | *dynamic* is a special type in Kotlin/JS. It basically turns off Kotlin's type checker. 4 | That is needed in order to interoperate with untyped or loosely typed environments, such 5 | as the JavaScript ecosystem. 6 | 7 | ```run-kotlin-js 8 | fun main(){ 9 | //sampleStart 10 | val a: dynamic = "abc" // 1 11 | val b: String = a // 2 12 | 13 | fun firstChar(s: String) = s[0] 14 | 15 | println("${firstChar(a)} == ${firstChar(b)}") // 3 16 | 17 | println("${a.charCodeAt(0, "dummy argument")} == ${b[0].toInt()}") // 4 18 | 19 | println(a.charAt(1).repeat(3)) // 5 20 | 21 | fun plus(v: dynamic) = v + 2 22 | 23 | println("2 + 2 = ${plus(2)}") // 6 24 | println("'2' + 2 = ${plus("2")}") 25 | //sampleEnd 26 | } 27 | ``` 28 | 29 | 1. Any value can be assigned to a `dynamic` variable type. 30 | 2. A dynamic value can be assigned to anything. 31 | 3. A dynamic variable can be passed as an argument to any function. 32 | 4. Any property or function with any arguments can be called on a `dynamic` variable. 33 | 5. A function call on a `dynamic` variable always returns a dynamic value, so it is possible to chain the calls. 34 | 6. Operators, assignments, and indexed access (`[..]`) are translated "as is". Beware! 35 | -------------------------------------------------------------------------------- /examples/09_Kotlin_JS/02_js_function.md: -------------------------------------------------------------------------------- 1 | # JS function 2 | 3 | You can inline JavaScript code into your Kotlin code using the js("…") function. 4 | This should be used with extreme care. 5 | 6 | 7 | ```run-kotlin-js 8 | fun main() { 9 | //sampleStart 10 | js("alert(\"alert from Kotlin!\")") // 1 11 | //sampleEnd 12 | } 13 | ``` 14 | 15 | 1. Sending a JavaScript alert from a Kotlin function. 16 | 17 | ```run-kotlin-js 18 | fun main(){ 19 | //sampleStart 20 | val json = js("{}") // 1 21 | json.name = "Jane" // 2 22 | json.hobby = "movies" 23 | 24 | println(JSON.stringify(json)) // 3 25 | //sampleEnd 26 | } 27 | ``` 28 | 29 | 1. Creates a JavaScript object literal. The `js(...)` function return type is `dynamic`. 30 | 2. Adds some properties by utilizing the `dynamic` type capabilities. 31 | 3. Passes the JSON to JavaScript API. 32 | -------------------------------------------------------------------------------- /examples/09_Kotlin_JS/03_external.md: -------------------------------------------------------------------------------- 1 | # External declarations 2 | 3 | *external* keyword allows to declare existing JavaScript API in a type-safe way. 4 | 5 | ```run-kotlin-js 6 | external fun alert(msg: String) // 1 7 | 8 | fun main() { 9 | alert("Hi!") // 2 10 | } 11 | ``` 12 | 13 | 1. Declares an existing JavaScript function `alert` which takes a single `String` argument. 14 | 2. Uses `alert` as if it were regular Kotlin. 15 | 16 | Note that Kotlin checks during compilation, that a single argument of type String is passed. 17 | Such a check prevents some bugs even when using pure JavaScript API. 18 | 19 | Please [refer to the docs](https://kotlinlang.org/docs/reference/js-interop.html#external-modifier) in order 20 | to learn more about describing existing JavaScript API. 21 | -------------------------------------------------------------------------------- /examples/09_Kotlin_JS/05_Canvas.md: -------------------------------------------------------------------------------- 1 | # Canvas (Hello Kotlin) 2 | 3 | The following example demonstrates usage of HTML5 Canvas from Kotlin. 4 | 5 | Here strange creatures are watching the kotlin logo. You can drag'n'drop them as well as the logo. Doubleclick to add more creatures but be careful. They may be watching you! 6 | 7 | ```run-kotlin-canvas 8 | package creatures 9 | 10 | import org.w3c.dom.* 11 | import org.w3c.dom.events.MouseEvent 12 | import kotlinx.browser.document 13 | import kotlinx.browser.window 14 | import kotlin.math.* 15 | 16 | 17 | fun getImage(path: String): HTMLImageElement { 18 | val image = window.document.createElement("img") as HTMLImageElement 19 | image.src = path 20 | return image 21 | } 22 | 23 | val canvas = initializeCanvas() 24 | 25 | fun initializeCanvas(): HTMLCanvasElement { 26 | val canvas = document.createElement("canvas") as HTMLCanvasElement 27 | val context = canvas.getContext("2d") as CanvasRenderingContext2D 28 | context.canvas.width = window.innerWidth.toInt() 29 | context.canvas.height = window.innerHeight.toInt() 30 | document.body!!.appendChild(canvas) 31 | return canvas 32 | } 33 | 34 | val context: CanvasRenderingContext2D 35 | get() { 36 | return canvas.getContext("2d") as CanvasRenderingContext2D 37 | } 38 | 39 | abstract class Shape() { 40 | 41 | abstract fun draw(state: CanvasState) 42 | // these two abstract methods defines that our shapes can be dragged 43 | operator abstract fun contains(mousePos: Vector): Boolean 44 | 45 | abstract var pos: Vector 46 | 47 | var selected: Boolean = false 48 | 49 | // a couple of helper extension methods we'll be using in the derived classes 50 | fun CanvasRenderingContext2D.shadowed(shadowOffset: Vector, alpha: Double, render: CanvasRenderingContext2D.() -> Unit) { 51 | save() 52 | shadowColor = "rgba(100, 100, 100, $alpha)" 53 | shadowBlur = 5.0 54 | shadowOffsetX = shadowOffset.x 55 | shadowOffsetY = shadowOffset.y 56 | render() 57 | restore() 58 | } 59 | 60 | fun CanvasRenderingContext2D.fillPath(constructPath: CanvasRenderingContext2D.() -> Unit) { 61 | beginPath() 62 | constructPath() 63 | closePath() 64 | fill() 65 | } 66 | } 67 | 68 | val logoImage by lazy { getImage("https://play.kotlinlang.org/assets/kotlin-logo.svg") } 69 | 70 | val logoImageSize = v(64.0, 64.0) 71 | 72 | val Kotlin = Logo(v(canvas.width / 2.0 - logoImageSize.x / 2.0 - 64, canvas.height / 2.0 - logoImageSize.y / 2.0 - 64)) 73 | 74 | class Logo(override var pos: Vector) : Shape() { 75 | val relSize: Double = 0.18 76 | val shadowOffset = v(-3.0, 3.0) 77 | var size: Vector = logoImageSize * relSize 78 | // get-only properties like this saves you lots of typing and are very expressive 79 | val position: Vector 80 | get() = if (selected) pos - shadowOffset else pos 81 | 82 | 83 | fun drawLogo(state: CanvasState) { 84 | if (!logoImage.complete) { 85 | state.changed = true 86 | return 87 | } 88 | 89 | size = logoImageSize * (state.size.x / logoImageSize.x) * relSize 90 | state.context.drawImage(getImage("https://play.kotlinlang.org/assets/kotlin-logo.svg"), 0.0, 0.0, 91 | logoImageSize.x, logoImageSize.y, 92 | position.x, position.y, 93 | size.x, size.y) 94 | } 95 | 96 | override fun draw(state: CanvasState) { 97 | val context = state.context 98 | if (selected) { 99 | // using helper we defined in Shape class 100 | context.shadowed(shadowOffset, 0.2) { 101 | drawLogo(state) 102 | } 103 | } else { 104 | drawLogo(state) 105 | } 106 | } 107 | 108 | override fun contains(mousePos: Vector): Boolean = mousePos.isInRect(pos, size) 109 | 110 | val centre: Vector 111 | get() = pos + size * 0.5 112 | } 113 | 114 | val gradientGenerator by lazy { RadialGradientGenerator(context) } 115 | 116 | class Creature(override var pos: Vector, val state: CanvasState) : Shape() { 117 | 118 | val shadowOffset = v(-5.0, 5.0) 119 | val colorStops = gradientGenerator.getNext() 120 | val relSize = 0.05 121 | // these properties have no backing fields and in java/javascript they could be represented as little helper functions 122 | val radius: Double 123 | get() = state.width * relSize 124 | val position: Vector 125 | get() = if (selected) pos - shadowOffset else pos 126 | val directionToLogo: Vector 127 | get() = (Kotlin.centre - position).normalized 128 | 129 | //notice how the infix call can make some expressions extremely expressive 130 | override fun contains(mousePos: Vector) = pos distanceTo mousePos < radius 131 | 132 | // defining more nice extension functions 133 | fun CanvasRenderingContext2D.circlePath(position: Vector, rad: Double) { 134 | arc(position.x, position.y, rad, 0.0, 2 * PI, false) 135 | } 136 | 137 | //notice we can use an extension function we just defined inside another extension function 138 | fun CanvasRenderingContext2D.fillCircle(position: Vector, rad: Double) { 139 | fillPath { 140 | circlePath(position, rad) 141 | } 142 | } 143 | 144 | override fun draw(state: CanvasState) { 145 | val context = state.context 146 | if (!selected) { 147 | drawCreature(context) 148 | } else { 149 | drawCreatureWithShadow(context) 150 | } 151 | } 152 | 153 | fun drawCreature(context: CanvasRenderingContext2D) { 154 | context.fillStyle = getGradient(context) 155 | context.fillPath { 156 | tailPath(context) 157 | circlePath(position, radius) 158 | } 159 | drawEye(context) 160 | } 161 | 162 | fun getGradient(context: CanvasRenderingContext2D): CanvasGradient { 163 | val gradientCentre = position + directionToLogo * (radius / 4) 164 | val gradient = context.createRadialGradient(gradientCentre.x, gradientCentre.y, 1.0, gradientCentre.x, gradientCentre.y, 2 * radius) 165 | for (colorStop in colorStops) { 166 | gradient.addColorStop(colorStop.first, colorStop.second) 167 | } 168 | return gradient 169 | } 170 | 171 | fun tailPath(context: CanvasRenderingContext2D) { 172 | val tailDirection = -directionToLogo 173 | val tailPos = position + tailDirection * radius * 1.0 174 | val tailSize = radius * 1.6 175 | val angle = PI / 6.0 176 | val p1 = tailPos + tailDirection.rotatedBy(angle) * tailSize 177 | val p2 = tailPos + tailDirection.rotatedBy(-angle) * tailSize 178 | val middlePoint = position + tailDirection * radius * 1.0 179 | context.moveTo(tailPos.x, tailPos.y) 180 | context.lineTo(p1.x, p1.y) 181 | context.quadraticCurveTo(middlePoint.x, middlePoint.y, p2.x, p2.y) 182 | context.lineTo(tailPos.x, tailPos.y) 183 | } 184 | 185 | fun drawEye(context: CanvasRenderingContext2D) { 186 | val eyePos = directionToLogo * radius * 0.6 + position 187 | val eyeRadius = radius / 3 188 | val eyeLidRadius = eyeRadius / 2 189 | context.fillStyle = "#FFFFFF" 190 | context.fillCircle(eyePos, eyeRadius) 191 | context.fillStyle = "#000000" 192 | context.fillCircle(eyePos, eyeLidRadius) 193 | } 194 | 195 | fun drawCreatureWithShadow(context: CanvasRenderingContext2D) { 196 | context.shadowed(shadowOffset, 0.7) { 197 | context.fillStyle = getGradient(context) 198 | fillPath { 199 | tailPath(context) 200 | context.circlePath(position, radius) 201 | } 202 | } 203 | drawEye(context) 204 | } 205 | } 206 | 207 | class CanvasState(val canvas: HTMLCanvasElement) { 208 | var width = canvas.width 209 | var height = canvas.height 210 | val size: Vector 211 | get() = v(width.toDouble(), height.toDouble()) 212 | val context = creatures.context 213 | var changed = true 214 | var shapes = mutableListOf() 215 | var selection: Shape? = null 216 | var dragOff = Vector() 217 | val interval = 1000 / 30 218 | 219 | init { 220 | canvas.onmousedown = { e: MouseEvent -> 221 | changed = true 222 | selection = null 223 | val mousePos = mousePos(e) 224 | for (shape in shapes) { 225 | if (mousePos in shape) { 226 | dragOff = mousePos - shape.pos 227 | shape.selected = true 228 | selection = shape 229 | break 230 | } 231 | } 232 | } 233 | 234 | canvas.onmousemove = { e: MouseEvent -> 235 | if (selection != null) { 236 | selection!!.pos = mousePos(e) - dragOff 237 | changed = true 238 | } 239 | } 240 | 241 | canvas.onmouseup = { e: MouseEvent -> 242 | if (selection != null) { 243 | selection!!.selected = false 244 | } 245 | selection = null 246 | changed = true 247 | this 248 | } 249 | 250 | canvas.ondblclick = { e: MouseEvent -> 251 | val newCreature = Creature(mousePos(e), this@CanvasState) 252 | addShape(newCreature) 253 | changed = true 254 | this 255 | } 256 | 257 | window.setInterval({ 258 | draw() 259 | }, interval) 260 | } 261 | 262 | fun mousePos(e: MouseEvent): Vector { 263 | var offset = Vector() 264 | var element: HTMLElement? = canvas 265 | while (element != null) { 266 | val el: HTMLElement = element 267 | offset += Vector(el.offsetLeft.toDouble(), el.offsetTop.toDouble()) 268 | element = el.offsetParent as HTMLElement? 269 | } 270 | return Vector(e.pageX, e.pageY) - offset 271 | } 272 | 273 | fun addShape(shape: Shape) { 274 | shapes.add(shape) 275 | changed = true 276 | } 277 | 278 | fun clear() { 279 | context.fillStyle = "#D0D0D0" 280 | context.fillRect(0.0, 0.0, width.toDouble(), height.toDouble()) 281 | context.strokeStyle = "#000000" 282 | context.lineWidth = 4.0 283 | context.strokeRect(0.0, 0.0, width.toDouble(), height.toDouble()) 284 | } 285 | 286 | fun draw() { 287 | if (!changed) return 288 | 289 | changed = false 290 | 291 | clear() 292 | for (shape in shapes.asReversed()) { 293 | shape.draw(this) 294 | } 295 | Kotlin.draw(this) 296 | } 297 | } 298 | 299 | class RadialGradientGenerator(val context: CanvasRenderingContext2D) { 300 | val gradients = mutableListOf>>() 301 | var current = 0 302 | 303 | fun newColorStops(vararg colorStops: Pair) { 304 | gradients.add(colorStops) 305 | } 306 | 307 | init { 308 | newColorStops(Pair(0.0, "#F59898"), Pair(0.5, "#F57373"), Pair(1.0, "#DB6B6B")) 309 | newColorStops(Pair(0.39, "rgb(140,167,209)"), Pair(0.7, "rgb(104,139,209)"), Pair(0.85, "rgb(67,122,217)")) 310 | newColorStops(Pair(0.0, "rgb(255,222,255)"), Pair(0.5, "rgb(255,185,222)"), Pair(1.0, "rgb(230,154,185)")) 311 | newColorStops(Pair(0.0, "rgb(255,209,114)"), Pair(0.5, "rgb(255,174,81)"), Pair(1.0, "rgb(241,145,54)")) 312 | newColorStops(Pair(0.0, "rgb(132,240,135)"), Pair(0.5, "rgb(91,240,96)"), Pair(1.0, "rgb(27,245,41)")) 313 | newColorStops(Pair(0.0, "rgb(250,147,250)"), Pair(0.5, "rgb(255,80,255)"), Pair(1.0, "rgb(250,0,217)")) 314 | } 315 | 316 | fun getNext(): Array> { 317 | val result = gradients.get(current) 318 | current = (current + 1) % gradients.size 319 | return result 320 | } 321 | } 322 | 323 | fun v(x: Double, y: Double) = Vector(x, y) 324 | 325 | class Vector(val x: Double = 0.0, val y: Double = 0.0) { 326 | operator fun plus(v: Vector) = v(x + v.x, y + v.y) 327 | operator fun unaryMinus() = v(-x, -y) 328 | operator fun minus(v: Vector) = v(x - v.x, y - v.y) 329 | operator fun times(koef: Double) = v(x * koef, y * koef) 330 | infix fun distanceTo(v: Vector) = sqrt((this - v).sqr) 331 | fun rotatedBy(theta: Double): Vector { 332 | val sin = sin(theta) 333 | val cos = cos(theta) 334 | return v(x * cos - y * sin, x * sin + y * cos) 335 | } 336 | 337 | fun isInRect(topLeft: Vector, size: Vector) = (x >= topLeft.x) && (x <= topLeft.x + size.x) && 338 | (y >= topLeft.y) && (y <= topLeft.y + size.y) 339 | 340 | val sqr: Double 341 | get() = x * x + y * y 342 | val normalized: Vector 343 | get() = this * (1.0 / sqrt(sqr)) 344 | } 345 | 346 | fun main(args: Array) { 347 | //sampleStart 348 | CanvasState(canvas).apply { 349 | addShape(Kotlin) 350 | addShape(Creature(size * 0.25, this)) 351 | addShape(Creature(size * 0.75, this)) 352 | } 353 | //sampleEnd 354 | } 355 | ``` 356 | -------------------------------------------------------------------------------- /examples/09_Kotlin_JS/06_HtmlBuilder.md: -------------------------------------------------------------------------------- 1 | # Html Builder 2 | 3 | Kotlin provides you with an option to describe structured data in a declarative style with _builders_. 4 | 5 | Below is an example of a type-safe Groovy-style builder. In this example, we will describe an HTML page in Kotlin. 6 | 7 | ```run-kotlin-canvas 8 | package html 9 | 10 | fun main() { 11 | //sampleStart 12 | val result = html { // 1 13 | head { // 2 14 | title { +"HTML encoding with Kotlin" } 15 | } 16 | body { // 2 17 | h1 { +"HTML encoding with Kotlin" } 18 | p { 19 | +"this format can be used as an" // 3 20 | +"alternative markup to HTML" // 3 21 | } 22 | 23 | // an element with attributes and text content 24 | a(href = "http://kotlinlang.org") { +"Kotlin" } 25 | 26 | // mixed content 27 | p { 28 | +"This is some" 29 | b { +"mixed" } 30 | +"text. For more see the" 31 | a(href = "http://kotlinlang.org") { 32 | +"Kotlin" 33 | } 34 | +"project" 35 | } 36 | p { 37 | +"some text" 38 | ul { 39 | for (i in 1..5) 40 | li { +"${i}*2 = ${i*2}" } 41 | } 42 | } 43 | } 44 | } 45 | //sampleEnd 46 | println(result) 47 | } 48 | 49 | interface Element { 50 | fun render(builder: StringBuilder, indent: String) 51 | } 52 | 53 | class TextElement(val text: String) : Element { 54 | override fun render(builder: StringBuilder, indent: String) { 55 | builder.append("$indent$text\n") 56 | } 57 | } 58 | 59 | @DslMarker 60 | annotation class HtmlTagMarker 61 | 62 | @HtmlTagMarker 63 | abstract class Tag(val name: String) : Element { 64 | val children = arrayListOf() 65 | val attributes = hashMapOf() 66 | 67 | protected fun initTag(tag: T, init: T.() -> Unit): T { 68 | tag.init() 69 | children.add(tag) 70 | return tag 71 | } 72 | 73 | override fun render(builder: StringBuilder, indent: String) { 74 | builder.append("$indent<$name${renderAttributes()}>\n") 75 | for (c in children) { 76 | c.render(builder, indent + " ") 77 | } 78 | builder.append("$indent\n") 79 | } 80 | 81 | private fun renderAttributes(): String { 82 | val builder = StringBuilder() 83 | for ((attr, value) in attributes) { 84 | builder.append(" $attr=\"$value\"") 85 | } 86 | return builder.toString() 87 | } 88 | 89 | override fun toString(): String { 90 | val builder = StringBuilder() 91 | render(builder, "") 92 | return builder.toString() 93 | } 94 | } 95 | 96 | abstract class TagWithText(name: String) : Tag(name) { 97 | operator fun String.unaryPlus() { 98 | children.add(TextElement(this)) 99 | } 100 | } 101 | class HTML() : TagWithText("html") { 102 | fun head(init: Head.() -> Unit) = initTag(Head(), init) 103 | fun body(init: Body.() -> Unit) = initTag(Body(), init) 104 | } 105 | 106 | 107 | class Head() : TagWithText("head") { 108 | fun title(init: Title.() -> Unit) = initTag(Title(), init) 109 | } 110 | 111 | class Title() : TagWithText("title") 112 | 113 | abstract class BodyTag(name: String) : TagWithText(name) { 114 | fun b(init: B.() -> Unit) = initTag(B(), init) 115 | fun p(init: P.() -> Unit) = initTag(P(), init) 116 | fun h1(init: H1.() -> Unit) = initTag(H1(), init) 117 | fun ul(init: UL.() -> Unit) = initTag(UL(), init) 118 | fun a(href: String, init: A.() -> Unit) { 119 | val a = initTag(A(), init) 120 | a.href = href 121 | } 122 | } 123 | 124 | class Body() : BodyTag("body") 125 | class UL() : BodyTag("ul") { 126 | fun li(init: LI.() -> Unit) = initTag(LI(), init) 127 | } 128 | 129 | class B() : BodyTag("b") 130 | class LI() : BodyTag("li") 131 | class P() : BodyTag("p") 132 | class H1() : BodyTag("h1") 133 | 134 | class A : BodyTag("a") { 135 | var href: String 136 | get() = attributes["href"]!! 137 | set(value) { 138 | attributes["href"] = value 139 | } 140 | } 141 | 142 | fun html(init: HTML.() -> Unit): HTML { 143 | val html = HTML() 144 | html.init() 145 | return html 146 | } 147 | 148 | ``` 149 | 150 | 1. `html` is actually a function call that takes a [lambda expression](http://kotlinlang.org/docs/reference/lambdas.html) as an argument. 151 | `html` function takes one parameter which is itself a function. 152 | The type of the function is `HTML.() -> Unit`, which is a function type with receiver. 153 | This means that we need to pass an instance of type `HTML` (a receiver) to the function, 154 | and we can call members of that instance inside the function. 155 | 156 | 2. `head` and `body` are member functions of the`HTML` class. 157 | 158 | 3. Adds the text to tags by calling the `unaryPlus()` operation, like `+"HTML encoding with Kotlin"`. 159 | 160 | For details see: [Type Safe Builders](http://kotlinlang.org/docs/reference/type-safe-builders.html) 161 | --------------------------------------------------------------------------------