48 KiB
48 KiB
Original repository: https://code.mipt.ru/SPC/education/ks-materials
Idiom 1, String interpolation¶
Interpolated string allows constructing string fast
In [ ]:
println("This is a plain string")
In [ ]:
val arg = "Interpolated" println("This is an $arg string")
In [ ]:
val number = 1 println("This is an $arg string number ${number + 1}")
In [ ]:
println("This is a string with \$")
In [ ]:
println(""" This is a multi line raw string """.trimIndent())
In [ ]:
println("""This is a raw string number ${number+1} with \ and ${'$'} """)
Idiom 2, val VS var¶
val
means read-only property
var
means writable property
Unnecessary use of var
is not recommended.
NOTE: In performance critical cases, explicit cycles are better, but mutable state should be encapsulated.
In [ ]:
/* Not recommended */ var counter = 0 for(i in 0..20){ counter += i } counter
In [ ]:
/* recommended */ val sum = (0..20).sum() sum
In [ ]:
val intArray: IntArray = IntArray(21) { it } intArray.sum()
Idiom 3 top level functions¶
In [ ]:
/** * Functions could be defined everywhere in kotlin code. This is a so-called top-level function. */ fun doSomething(){ println("I did it") } doSomething()
In [ ]:
object AnObject{ /** * This is a member-function */ fun doSomethingInAnObject(){ println("I did it in an object") } } AnObject.doSomethingInAnObject()
In [ ]:
fun doSomethingSpecial(){ val special = "special" /** * This one is inside another function */ fun doSomethingInside(){ println("I did $special inside another function") } doSomethingInside() } doSomethingSpecial()
In [ ]:
fun returnFunction(): (String) -> Unit{ fun printStringWithPrefix(str: String){ println("Prefixed: $str") } return ::printStringWithPrefix // return { println("Prefixed: $it") } } returnFunction()("Haskel, Boo!")
In [ ]:
//idiom 3 - Unit fun returnUnit(): Unit{ // no return statement `Unit` is returned implicitly }
In [ ]:
//idiom 4 - default parameters fun doSomethingWithDefault(a: Int = 0, b: String = ""): String { return "A string with a == $a and b == \"$b\"" } doSomethingWithDefault(4, b = "fff")
In [ ]:
//idiom 5 - function body shortcut fun theSameAsBefore(a: Int = 0, b: String = ""): String = "A string with a == $a and b == $b"
Idiom 6¶
In [ ]:
//Interfaces and objects interface AnInterface { val a: Int get() = 4 // set(value){ // println(value) // } fun doSomething() } class AClass(override val a: Int) : AnInterface { override fun doSomething() = TODO() } /** * A singleton (object) is a type (class) which have only one instance */ object AnObject : AnInterface { override fun doSomething(): Unit = TODO("Not yet implemented") } /** * Creating an instance */ val instance = AClass(3) /** * Using singleton reference without constructor invocation */ val obj: AnInterface = AnObject /** * Anonymous object */ val anonymous = object : AnInterface { override fun doSomething(): Unit = TODO("Not yet implemented") } /** * The one that should not be named */ val voldemort = object { fun doSomething(): Unit = TODO() } voldemort.doSomething()
Idiom 7¶
Runtime type dispatch
In [ ]:
/** * Matching by type */ fun checkType(arg: Any): Unit = when(arg){ is String -> println("I am a String") is Int -> println("I am an Int") is Double -> println("I am a Double") // 2==2 -> println("Wat?") else -> println("I don't know who am I?") } fun checkType2(arg: Any): Unit = when{ arg is String -> println("I am a String") arg is Int -> println("I am an Int") arg is Double -> println("I am a Double") 2==2 -> println("Wat?") else -> println("I don't know who am I?") } checkType2(true)
Idiom 8¶
In [ ]:
class SimpleClass(val a: Int, val b: Double, c: Double) data class DataClass(val a: Int, val b: Double) { val c get() = b + 1 } val simple = SimpleClass(1, 2.0, 3.0) println(simple) val data = DataClass(2, 2.0) val copy = data.copy(b = 22.2) println(copy)
Idiom 9¶
In [ ]:
/** * The declaration if valid because [TODO] returns Nothing */ fun getSomething(): Int? = TODO() fun findSomething(): Int { // early return is valid because `return` operator result is Nothing val found = getSomething() ?: return 2 return found + 2 } fun checkCondition(): Int { fun conditionSatisfied() = false return if (conditionSatisfied()) { 1 } else { //error is Nothing error("Condition is not satisfied") } }
In [ ]:
/** * idiom 10 * Nullable truth. */ fun idiom10() { class A(val b: Boolean?) val a: A? = A(null) // Compilator warning here //use a?.b == true //instead of a?.b ?: false // The old way val res = if (a == null) { false } else { if (a.b == null) { false } else { a.b } } }
In [ ]:
/** * Dart-like (?=) nullable assignment */ fun idiom11() { var x: String? = null x = x ?: "Hello" }
In [ ]:
/** * Safe call and elvis operator */ fun idiom12() { val files = File("Test").listFiles() println(files?.size ?: "empty") } idiom12()
Idiom 13¶
In [ ]:
fun printNotNull(any: Any) = println(any) // printNotNull(null) does not work val value: Int? = 2 //val value: Int? by lazy{ 2 } //printNotNull(value) // Error if (value != null) { //not guaranteed to work with mutable variable printNotNull(value) } var value1: Int? = 2 // Safe call here value1?.let { printNotNull(it) // execute this block if not null //printNotNull(value1) // value is not null here }
Idiom 14: Extension functions¶
In [ ]:
/** * Extension function on a String. It is not monkey-patching. */ fun String.countOs(): Int = count { it == 'о' } // implicit this points to String fun Int.printMe() = println(this) // explicit this "вылысыпыдыстычка".countOs().printMe()
In [ ]:
infix fun <T: Comparable<T>> ClosedRange<T>.intersect(other: ClosedRange<T>): ClosedRange<T>?{ val start = maxOf(this.start, other.start) val end = minOf(this.endInclusive, other.endInclusive) return if(end>=start) start..end else null } 0..8 intersect 6..12
15
In [ ]:
/** * Extension property (must be virtual) */ val List<Int>.odd get() = filter { it % 2 == 1 } /** * Extension variable (also must be virtual) */ var Array<Int>.second: Int get() = this[1] set(value) { this[1] = value } val array = Array(5){it} array.second = 9 array
Scope functions (run, with, let)¶
In [ ]:
object AClass{ val a = "a" val b = "b" val c = "c" } fun getAClass(): AClass? = null /** * [with]/[run]/[let] are used to create a scope with given argument or receiver */ // Simple print of AClass println("a = ${AClass.a}, b = ${AClass.b}, c = ${AClass.c}") // Using `with` val res = with(AClass){ // AClass type is the receiver in this scope println("a = $a, b = $b, c = $c") return@with "some value" } res
In [ ]:
//using `run` getAClass()?.run { // the same as above println("a = $a, b= $b, c = $c") } //Using `let` to compose result. Not recommended using without a need val letResult = getAClass()?.let { arg -> arg.c + arg.a }
Scope functions (also, apply)¶
In [ ]:
class Rectangle{ var length: Number = 0 var breadth: Number=0 var color: Int = 0xffffff } /** * Using apply/also to add a finalizing action */ var i = 2 /** * [also] block does not affect the result */ fun getAndIncrement() = i.also { i += 1 } println(getAndIncrement()) println(i) /** * Configure properties of an object (apply) * https://kotlinlang.org/docs/idioms.html#configure-properties-of-an-object-apply */ val myRectangle = Rectangle().apply { length = 4 breadth = 5 color = 0xFAFAFA }
In [ ]:
class A() { inner class B{ fun doSomething(){ println(this) println(this@A) } } } A().B().doSomething()
In [ ]:
Idiom 18 lists¶
In [ ]:
/** * Lists and mutable lists */ /** * This method creates a read-only list of strings. One should note that the type of the list is not specified */ val list = listOf("a", "b", "c") println(list[0]) println(list.get(1)) println(list.getOrNull(4) ?: "nothing")
In [ ]:
/** * This one creates a mutable list */ val mutableList: MutableList<String> = mutableListOf("a", "b", "c") mutableList[2] = "d" mutableList.add("e") mutableList += "f" mutableList
In [ ]:
/** * This one creates a mutable ArrayList. */ val arrayList: ArrayList<String> = arrayListOf("a", "b", "c") //Danger zone val newList: List<String> = list + "f" + mutableList println(newList)
In [ ]:
//Bonus val lambdaList = List(3){ it.toString() } println(lambdaList) val builderList = buildList{ add(2) add(8) remove(8) } builderList
In [ ]:
// val sequentialList = List(20){it} // sequentialList.zipWithNext{ l, r -> r - l }.forEach{println(it)}
In [ ]:
/** * idiom 19 * Use shortcut function to provide default values */ fun doSomething(additionalArguments: List<String> = emptyList()){ TODO() emptyArray<String>() emptySet<String>() emptyMap<String,String>() }
Idiom 20¶
In [ ]:
val map = mutableMapOf( "key" to "a", "key2" to "b", ) //The map could be accessed via indexing operation println(map["key"]) map["key"] = "fff"
In [ ]:
//val entry: MutableMap.MutableEntry<String, String> = map.iterator().next() /** * The destructuring declaration for maps and other objects that support `operator fun componentN` */ for ((k, v) in map) { //val (k, v) = entry // val k = entry.component1() // val v = entry.component2() println("$k -> $v") }
In [ ]:
map.forEach { (k, v) -> println("$k -> $v")}
In [ ]:
val (a, b) = Pair(1, 2) val coord = doubleArrayOf(0.0, 1.0, 2.0) val (x,y,z) = coord data class Coordinate(val x: Double, val y: Int) val (x1, y1) = Coordinate(1.0,2)
Idiom 22 Mutable type access decorator¶
In [ ]:
class AClassWithList{ var b: Double = 2.0 private set init{ b = 5.0 } private val _list: MutableList<Int> = ArrayList<Int>() val list: List<Int> get() = _list } val obj = AClassWithList() // obj.b = 10.0 //error obj.list
Idiom 23¶
In [ ]:
val emailsList = emptyList<String>() // When used directly infix in operator checks if the element is contained in a collection //using `operator fun contains` if ("john@example.com" in emailsList) { println("is in list") } if ("jane@example.com" !in emailsList) { println("not in list") }
In [ ]:
class DateRange(val start: Instant, val end: Instant) operator fun DateRange.contains(value: Instant): Boolean = value > start && value < end println(Instant.now() in DateRange(Instant.EPOCH, Instant.MAX))
In [ ]:
// Another (different) use of `in` is iteration over range or collection using // using `operator fun iterator` for (i in 1..100) { println(i) } // closed range: includes 100 (1..100).forEach { i -> println(i) } //the same, but with boxing for (i in 1 until 100) { println(i) } // half-open range: does not include 100 for (x in 2..10 step 2) { println(x) } for (x in 10 downTo 1) { println(x) } infix fun ClosedRange<Double>.step(step: Double): Sequence<Double> { //TODO check arguments var current = start return sequence { do { yield(current) current += step } while (current <= endInclusive) } } for (x in 0.0..10.0 step 0.5){ println(x) }
Idiom 25 default arguments¶
In [ ]:
fun integrate( from: Double = 0.0, to: Double = 1.0, step: Double = 0.01, function: (Double) -> Double, ): Double { require(to > from) require(step > 0) var sum = 0.0 var pos = from while (pos < to) { sum += function(pos) pos += step } return sum*step } integrate { x -> x * x + 2 * x + 1 } integrate(0.0, PI) { sin(it) } integrate(0.0, step = 0.02, to = PI) { sin(it) } //integrate(0.0, step = 0.02, PI) { sin(it) }
Idiom 26 map-reduce¶
In [ ]:
val list: List<Int> = listOf(1, 2, 3, 4, 5, 6) val result = list //.stream().parallel() //.asSequence() .filter { it % 2 == 0 } //select even numbers .map { it * it } // get square of each element //.onEach { println(it) } //.sumOf { it } //use one of reduce operations .reduce { acc: Int, i: Int -> acc + i } result
Idiom 28 Wrap mutable logic¶
In [ ]:
val list = buildList { repeat(10){ add(it) } } println(list)
Idiom 29 resource usage¶
In [ ]:
val stream = Files.newInputStream(Paths.get("file.txt")) // The resource is automatically closed when leaving the scope stream.bufferedReader().use { reader -> println(reader.readText()) }
Idiom 30 Factory as parameter and companion factories¶
In [ ]:
interface Factory<T : Any> { fun build(str: String): T } class IntContainer(val arg: Int) { companion object : Factory<IntContainer> { override fun build(str: String) = IntContainer(str.toInt()) } } class DoubleContainer(val arg: Double) { companion object : Factory<DoubleContainer> { override fun build(str: String) = DoubleContainer(str.toDouble()) } } fun <T : Any> buildContainer(str: String, factory: Factory<T>): T = factory.build(str) buildContainer("22", IntContainer)
Idiom 31 initialization¶
In [ ]:
open class Bad{ val value: Int init { value = requestValue() } open fun requestValue(): Int { doSomethingElse() return 2 } private fun doSomethingElse(){ println(value) } } Bad()
In [ ]:
//Factory functions are preferred to the initialization logic data class Good internal constructor(val value: Int){ init { //Initialization block is there to check arguments require(value >= 0) } companion object } fun requestValue(): Int = TODO() // This is the factory-function fun Good() = Good(requestValue()) // additional constructor-like builders could be added to the companion @OptIn(ExperimentalUnsignedTypes::class) fun Good.Companion.build(value: UInt) = Good(value.toInt()) Good.build(32U)
Idiom 32 Delegates¶
In [ ]:
class ClassWithALazyProperty{ //Use lazy delegate for something that should be calculated ones on first call val lazyValue by lazy { //Do dome heavy logic here println("Initialized") 22 } val getterValue:Int get(){ println("got") return 33 } } val lazyClass = ClassWithALazyProperty() lazyClass.lazyValue lazyClass.lazyValue lazyClass.getterValue lazyClass.getterValue
In [ ]:
//Using other delegates val map = mapOf("a" to 1, "b" to 2) val a: Int by map println(a)
Idiom 33 Inline functions¶
In [ ]:
/** * Definition of inline function */ inline fun List<Int>.forEachOdd(block: (Int) -> Unit) = forEach { if (it % 2 == 1) block(it) } /** * The demonstration of use of inline [forEach] function with non-local return */ fun foo() { listOf(1, 2, 3, 4, 5).forEach { if (it == 3) return // non-local return directly to the caller of foo() print("$it, ") } println("this point is unreachable") } foo()
In [ ]:
/** * Using inline function for type reification during the compile time */ inline fun <reified T> List<T>.prettyPrint() = forEach { when (T::class) { Double::class -> println("Double: ${it as Double}") Int::class -> println("Int: ${it as Int}") else -> it.toString() } } inline fun <reified T> prettyPrintOne(arg: T) = listOf(arg).prettyPrint() /** * **WARNING** inline functions are an advanced feature and should be used only for * reification or non-local return * NOT for optimization. */
Idiom 34 Collections and boxing¶
In [ ]:
/** * Values are boxed. Each call is indirect */ val list: List<Double> = List(20) { it.toDouble() } /** * Still boxed */ val genericArray: Array<Double> = Array(20) { it.toDouble() } /** * Non-boxed */ val specializedArray: DoubleArray = DoubleArray(20) { it.toDouble() }
In [ ]: