{ "cells": [ { "cell_type": "markdown", "source": "* Official list of idioms: https://kotlinlang.org/docs/idioms.html\n", "attachments": {}, "metadata": { "datalore": { "node_id": "e5U9H2j8YPqeTklThKWtLy", "type": "MD", "hide_input_from_viewers": false, "hide_output_from_viewers": false, "report_properties": { "rowId": "xIF3Up82GVsiV1Ceze6Si3", "relativeY": 0 } } } }, { "cell_type": "markdown", "source": [ "## Preamble/Important notes\n", "\n", "* \"Syntactic sugar\" is a false concept. Each language feature affects how we use the language.\n", "* One can adopt different programming styles in Kotlin. Idiomatic kotlin is an unofficial combination of practices developed by experienced Kotlin users.\n", "* One of the key features of idiomatic kotlin is to use scopes instead of objects to provide incapsulation and limit visible mutable state.\n", "\n", "### What is idiomatic Kotlin (roughly)?\n", "\n", "* Less \"ceremony\". Pragmatic approach.\n", "* Hybrid object/functional paradigm (objects for data, functions for interactions).\n", "* Don't use mutable state wherever read-only is enough.\n", "* Don't use classes when functions are enough.\n", "* Don't use inheritance when composition is enough.\n", "* Function is a first-class citizen.\n", "* Scope is a first-class citizen.\n", "* Use extensions to separate inner workings from utilities.\n", "\n", "### Do not do\n", "\n", "* Scope pollution.\n", "* Scope function abuse.\n", "* Rely on initialization order." ], "attachments": {}, "metadata": { "datalore": { "node_id": "ARXeE5sXcjig7Oxpa5hdTG", "type": "MD", "hide_input_from_viewers": true, "hide_output_from_viewers": true } } }, { "cell_type": "markdown", "source": [ "## String interpolation\n", "\n", "Interpolated string allows constructing string fast" ], "attachments": {}, "metadata": { "datalore": { "node_id": "LVICJjSl3FQ9CWiBWDaNlp", "type": "MD", "hide_input_from_viewers": false, "hide_output_from_viewers": false, "report_properties": { "rowId": "xIF3Up82GVsiV1Ceze6Si3", "relativeY": 6 } } } }, { "cell_type": "code", "source": [ "println(\"This is a plain string\")" ], "metadata": { "datalore": { "node_id": "FFW0GaV6uhemvZxlpnjpFL", "type": "CODE", "hide_input_from_viewers": false, "hide_output_from_viewers": false, "report_properties": { "rowId": "ypTKLK2KvAZfnMbTgaVqNK", "relativeY": 0 } } }, "outputs": [], "execution_count": null }, { "cell_type": "code", "source": [ "val arg = \"Interpolated\"\n", "println(\"This is an $arg string\")" ], "metadata": { "datalore": { "node_id": "ZsQ4OOCMRSSkquucGThKYh", "type": "CODE", "hide_input_from_viewers": false, "hide_output_from_viewers": false, "report_properties": { "rowId": "qyGLHGsLl6mRCaOjpZBTD3", "relativeY": 0 } } }, "outputs": [], "execution_count": null }, { "cell_type": "code", "source": [ "val number = 1\n", "\n", "fun increment(num: Int) = num + 1\n", "println(\"This is an $arg string number ${increment(number) + 1}\")" ], "metadata": { "datalore": { "node_id": "7V2Dsj0FwQaSqFJcYcWsz9", "type": "CODE", "hide_input_from_viewers": false, "hide_output_from_viewers": false, "report_properties": { "rowId": "7tql3kbsQ9JIbbEfghi48B", "relativeY": 0 } } }, "outputs": [], "execution_count": null }, { "cell_type": "code", "source": [ "println(\"This is a string with \\$\")" ], "metadata": { "datalore": { "node_id": "hm9iYlWj1B2S0469GtxDrf", "type": "CODE", "hide_input_from_viewers": false, "hide_output_from_viewers": false, "report_properties": { "rowId": "BQz86nEWOj77fxXQ8iS47U", "relativeY": 0 } } }, "outputs": [], "execution_count": null }, { "cell_type": "code", "source": [ "println(\"\"\"\n", " This is a \n", " multi\n", " line\n", " raw\n", " string\n", "\"\"\".trimIndent())" ], "metadata": { "datalore": { "node_id": "h6GsBoqNfrx3k1edy3hjKZ", "type": "CODE", "hide_input_from_viewers": false, "hide_output_from_viewers": false, "report_properties": { "rowId": "RTl3zV3tStzlLoIHSvEZiV", "relativeY": 0 } } }, "outputs": [], "execution_count": null }, { "cell_type": "code", "source": [ "println(\"\"\"This is a raw string number ${number+1} with \\ and ${'$'} \"\"\")" ], "metadata": { "datalore": { "node_id": "KDL3D9lOVn6vX3iTAY50gb", "type": "CODE", "hide_input_from_viewers": false, "hide_output_from_viewers": false, "report_properties": { "rowId": "xmBQdVRhRGLsw0t87vxQW7", "relativeY": 0 } } }, "outputs": [], "execution_count": null }, { "cell_type": "markdown", "source": [ "## Make `val`, not `var`!\n", "\n", "`val` means read-only property\n", "\n", "`var` means writable property\n", "\n", "Unnecessary use of `var` is not recommended.\n", "\n", "**NOTE:** In performance critical cases, explicit cycles are better, but mutable state should be encapsulated." ], "attachments": {}, "metadata": { "datalore": { "node_id": "mz5BUTYWXHfIAjGOyinhqi", "type": "MD", "hide_input_from_viewers": false, "hide_output_from_viewers": false, "report_properties": { "rowId": "MpSiMp90k75zOtoELK5XSp", "relativeY": 0 } } } }, { "cell_type": "code", "source": [ "/* Not recommended */\n", "var counter = 0\n", "for(i in 0..20){\n", " counter += i\n", "}\n", "counter" ], "metadata": { "datalore": { "node_id": "KyyAj8GiWSE3ZOFi2Ct8pD", "type": "CODE", "hide_input_from_viewers": false, "hide_output_from_viewers": false, "report_properties": { "rowId": "vqa5BMfmP7oZwt8sdVLKi8", "relativeY": 0 } } }, "outputs": [], "execution_count": null }, { "cell_type": "code", "source": [ "/* recommended */\n", "val sum = (0..20).sum()\n", "sum" ], "metadata": { "datalore": { "node_id": "NKkj9bAwRFxueaMe7iiov6", "type": "CODE", "hide_input_from_viewers": false, "hide_output_from_viewers": false, "report_properties": { "rowId": "vqa5BMfmP7oZwt8sdVLKi8", "relativeY": 6 } } }, "outputs": [], "execution_count": null }, { "cell_type": "code", "source": [ "val intArray: IntArray = IntArray(21){ it -> it + 1 }\n", "intArray.sum()" ], "metadata": { "datalore": { "node_id": "icUKtPZGuhHjb95pFh77cl", "type": "CODE", "hide_input_from_viewers": false, "hide_output_from_viewers": false, "report_properties": { "rowId": "vqa5BMfmP7oZwt8sdVLKi8", "relativeY": 12 } } }, "outputs": [], "execution_count": null }, { "cell_type": "markdown", "source": [ "## Top level functions\n", "\n", "Don't create a class when a function is enough.\n", "\n", "Use access modifiers for incapsulation." ], "attachments": {}, "metadata": { "datalore": { "node_id": "PSmFAqLCw6ko0q3IWbHk4x", "type": "MD", "hide_input_from_viewers": false, "hide_output_from_viewers": false, "report_properties": { "rowId": "vqa5BMfmP7oZwt8sdVLKi8", "relativeY": 18 } } } }, { "cell_type": "code", "source": [ "/**\n", " * Functions could be defined everywhere in kotlin code. This is a so-called top-level function.\n", " */\n", "fun doSomething(){\n", " println(\"I did it\")\n", "}\n", "\n", "doSomething()" ], "metadata": { "datalore": { "node_id": "pKnOC6YRLvZ7Xj6SWth2bP", "type": "CODE", "hide_input_from_viewers": false, "hide_output_from_viewers": false, "report_properties": { "rowId": "vqa5BMfmP7oZwt8sdVLKi8", "relativeY": 24 } } }, "outputs": [], "execution_count": null }, { "cell_type": "code", "source": [ "interface AnInterface{\n", " fun doSomethingInAnObject()\n", "}\n", "\n", "object AnObject : AnInterface{\n", " /**\n", " * This is a member-function\n", " */\n", " override fun doSomethingInAnObject(){\n", " println(\"I did it in an object\")\n", " }\n", "}\n", "\n", "AnObject.doSomethingInAnObject()" ], "metadata": { "datalore": { "node_id": "z1PRFmypCJbpbsxEaiVol5", "type": "CODE", "hide_input_from_viewers": false, "hide_output_from_viewers": false, "report_properties": { "rowId": "vqa5BMfmP7oZwt8sdVLKi8", "relativeY": 30 } } }, "outputs": [], "execution_count": null }, { "cell_type": "code", "source": [ "fun doSomethingSpecial(){\n", " val base = \"base\"\n", " val special = \"special\"\n", " /**\n", " * This one is inside another function\n", " */\n", " fun String.doSomethingInside(){\n", " println(\"I did $special inside another function on $this\")\n", " }\n", " base.doSomethingInside()\n", "}\n", "//\"ddd\".doSomethingInside()\n", "\n", "doSomethingSpecial()" ], "metadata": { "datalore": { "node_id": "w8hub7Az3lxh78KXeq7Cpv", "type": "CODE", "hide_input_from_viewers": false, "hide_output_from_viewers": false, "report_properties": { "rowId": "vqa5BMfmP7oZwt8sdVLKi8", "relativeY": 36 } } }, "outputs": [], "execution_count": null }, { "cell_type": "code", "source": [ "fun returnFunction(): (String) -> Unit{\n", " fun printStringWithPrefix(str: String){\n", " println(\"Prefixed: $str\")\n", " }\n", " return ::printStringWithPrefix\n", " // return { printStringWithPrefix(it) }\n", " // return { println(\"Prefixed: $it\") }\n", "}\n", "\n", "returnFunction()(\"Haskel, Boo!\")" ], "metadata": { "datalore": { "node_id": "QxeCTOYkFSZi5tvWoUvRwU", "type": "CODE", "hide_input_from_viewers": false, "hide_output_from_viewers": false, "report_properties": { "rowId": "vqa5BMfmP7oZwt8sdVLKi8", "relativeY": 42 } } }, "outputs": [], "execution_count": null }, { "cell_type": "markdown", "source": [ "## Unit" ], "attachments": {}, "metadata": { "datalore": { "node_id": "0zwWQ2uPSUrlAoyy1k0GlZ", "type": "MD", "hide_input_from_viewers": false, "hide_output_from_viewers": false, "report_properties": { "rowId": "vqa5BMfmP7oZwt8sdVLKi8", "relativeY": 48 } } } }, { "cell_type": "code", "source": [ "\n", "fun returnUnit(): Unit{\n", " // no return statement `Unit` is returned implicitly\n", " val b = Unit\n", "}" ], "metadata": { "datalore": { "node_id": "UxEwPmJN8zFnMxBiL1Kd2W", "type": "CODE", "hide_input_from_viewers": false, "hide_output_from_viewers": false, "report_properties": { "rowId": "vqa5BMfmP7oZwt8sdVLKi8", "relativeY": 54 } } }, "outputs": [], "execution_count": null }, { "cell_type": "markdown", "source": [ "## Default parameters" ], "attachments": {}, "metadata": { "datalore": { "node_id": "2ZAyWaX4qnYZefjc4US2Yj", "type": "MD", "hide_input_from_viewers": true, "hide_output_from_viewers": true } } }, { "cell_type": "code", "source": [ "fun doSomethingWithDefault(a: Int = 0, b: String = \"\") {\n", " println(\"A string with a == $a and b == \\\"$b\\\"\")\n", "}\n", "\n", "doSomethingWithDefault()\n", "doSomethingWithDefault(2, \"aaa\")\n", "doSomethingWithDefault(b = \"fff\", a = 8)\n", "doSomethingWithDefault(4, b = \"fff\")\n", "doSomethingWithDefault(a = 2, \"fff\")// don't do that" ], "metadata": { "datalore": { "node_id": "Pds7b8enkYPmA9KAaO20zI", "type": "CODE", "hide_input_from_viewers": false, "hide_output_from_viewers": false, "report_properties": { "rowId": "vqa5BMfmP7oZwt8sdVLKi8", "relativeY": 60 } } }, "outputs": [], "execution_count": null }, { "cell_type": "code", "source": [ "fun integrate(\n", " from: Double = 0.0,\n", " to: Double = 1.0,\n", " step: Double = 0.01,\n", " function: (Double) -> Double,\n", "): Double {\n", " require(to > from)\n", " require(step > 0)\n", " var sum = 0.0\n", " var pos = from\n", " while (pos < to) {\n", " sum += function(pos)\n", " pos += step\n", " }\n", " return sum*step\n", "}\n", "\n", "integrate { x ->\n", " x * x + 2 * x + 1\n", "}\n", "integrate(0.0, PI) { sin(it) }\n", "integrate(0.0, step = 0.02, to = PI) { sin(it) }\n", "//integrate(0.0, step = 0.02, PI) { sin(it) }" ], "metadata": { "datalore": { "node_id": "LT2YprLmzrgVcyBo3GKilz", "type": "CODE", "hide_input_from_viewers": true, "hide_output_from_viewers": true } }, "outputs": [], "execution_count": null }, { "cell_type": "markdown", "source": [ "## Function body shortcut" ], "attachments": {}, "metadata": { "datalore": { "node_id": "lAXRHN4vpo98h93qMABm3D", "type": "MD", "hide_input_from_viewers": true, "hide_output_from_viewers": true } } }, { "cell_type": "code", "source": [ "fun theSameAsBefore(a: Int = 0, b: String = \"\"): String = \"A string with a == $a and b == $b\"\n", "\n", "class Context(val a: String, val b: String)\n", "\n", "fun somethingWith(context: Context) = with(context){\n", " println(a + b)\n", "}" ], "metadata": { "datalore": { "node_id": "wEpzn9oKJBKKsBDGytj92U", "type": "CODE", "hide_input_from_viewers": false, "hide_output_from_viewers": false, "report_properties": { "rowId": "vqa5BMfmP7oZwt8sdVLKi8", "relativeY": 66 } } }, "outputs": [], "execution_count": null }, { "cell_type": "markdown", "source": [ "## Trailing lambda\n", "\n", "Scope is a separate important concept in Kotlin" ], "attachments": {}, "metadata": { "datalore": { "node_id": "YWQvNkVDKL8m8jqIg1bwLu", "type": "MD", "hide_input_from_viewers": true, "hide_output_from_viewers": true } } }, { "cell_type": "code", "source": [ "data class BuildResult internal constructor(val str: String, val int: Int, val optional: Double?){\n", " init{\n", " require(optional!= null){\"null!\"}\n", " }\n", "}\n", "\n", "class Builder(\n", " var str: String = \"\", \n", " var int: Int = 0\n", "){\n", " var optional: Double? = null\n", "\n", " fun build() = BuildResult(str, int, optional)\n", "}\n", "\n", "fun doWithBuilder(defaultInt: Int, block: Builder.() -> Unit): BuildResult = \n", " Builder(int = defaultInt).apply(block).build()\n", "\n", "doWithBuilder(2){\n", " str = \"ff\"\n", " optional = 1.0\n", "}" ], "metadata": { "datalore": { "node_id": "0KGHAp1kvSWJaLGhdf5xk2", "type": "CODE", "hide_input_from_viewers": true, "hide_output_from_viewers": true } }, "outputs": [], "execution_count": null }, { "cell_type": "markdown", "source": [ "## Inheritance" ], "attachments": {}, "metadata": { "datalore": { "node_id": "qBWuWasNK9nrp0iqoE1ZPA", "type": "MD", "hide_input_from_viewers": true, "hide_output_from_viewers": true } } }, { "cell_type": "code", "source": [ "//Interfaces and objects\n", "\n", "interface AnInterface {\n", " val a: Int\n", " get() = 4\n", " // set(value){\n", " // println(value)\n", " // }\n", " fun doSomething() //= println(\"From interface\")\n", "}\n", "\n", "abstract class AnAbstractClass(override val a: Int): AnInterface{\n", " override final fun doSomething() = println(\"From a class\")\n", " abstract fun doSomethingElse()\n", "}\n", "\n", "class AClass(override val a: Int) : AnInterface {\n", " override fun doSomething() = println(\"From a class\")\n", "}\n", "\n", "class BClass(a: Int) : AnAbstractClass(a) {\n", " override fun doSomethingElse() = println(\"something else\")\n", "}\n", "\n", "/**\n", " * A singleton (object) is a type (class) which have only one instance\n", " */\n", "object AnObject : AnInterface {\n", " override fun doSomething(): Unit = TODO(\"Not yet implemented\")\n", "}\n", "\n", "/**\n", "* Creating an instance\n", "*/\n", "val instance = AClass(3)\n", "\n", "/**\n", "* Using singleton reference without constructor invocation\n", "*/\n", "val obj: AnInterface = AnObject\n", "\n", "/**\n", "* Anonymous object\n", "*/\n", "val anonymous = object : AnInterface {\n", " override fun doSomething(): Unit = TODO(\"Not yet implemented\")\n", "}\n", "\n", "/**\n", "* The one that should not be named\n", "*/\n", "val voldemort = object {\n", " fun doSomething(): Unit = TODO()\n", "}\n", "\n", "//voldemort.doSomething()" ], "metadata": { "datalore": { "node_id": "aW9cBvDwbbUsi2CBRrXDPB", "type": "CODE", "hide_input_from_viewers": true, "hide_output_from_viewers": true } }, "outputs": [], "execution_count": null }, { "cell_type": "markdown", "source": [ "## Declaration site variance\n", "\n", "In consumes, out produces." ], "attachments": {}, "metadata": { "datalore": { "node_id": "IvTjF8aNlaknQt9252Tzwl", "type": "MD", "hide_input_from_viewers": true, "hide_output_from_viewers": true } } }, { "cell_type": "code", "source": [ "interface Producer{\n", " fun produce(): T\n", "}\n", "\n", "interface Consumer{\n", " fun consume(value: T)\n", "}" ], "metadata": { "datalore": { "node_id": "O0HQehHaiD0QUaFi62N6C2", "type": "CODE", "hide_input_from_viewers": true, "hide_output_from_viewers": true } }, "outputs": [], "execution_count": null }, { "cell_type": "markdown", "source": [ "## Return on when\n", "https://kotlinlang.org/docs/idioms.html#return-on-when-statement" ], "attachments": {}, "metadata": { "datalore": { "node_id": "YDCyDwHaw8LGUsRYGvgnRA", "type": "MD", "hide_input_from_viewers": true, "hide_output_from_viewers": true } } }, { "cell_type": "code", "source": [ "fun transform(color: String): Int = when (color) {\n", " \"Red\" -> 0\n", " \"Green\" -> 1\n", " \"Blue\" -> 2\n", " else -> throw IllegalArgumentException(\"Invalid color param value\")\n", "}" ], "metadata": { "datalore": { "node_id": "fCgUSk32WDSfR1GjIKa4tH", "type": "CODE", "hide_input_from_viewers": true, "hide_output_from_viewers": true } }, "outputs": [], "execution_count": null }, { "cell_type": "markdown", "source": [ "## Runtime type dispatch\n", "\n", "In Java it is called DOP (Data Oriented Programming).\n", "\n", "Also, sometimes (wrongly) called pattern matching." ], "attachments": {}, "metadata": { "datalore": { "node_id": "GI03Lj2Li76ORUHS53nTIx", "type": "MD", "hide_input_from_viewers": true, "hide_output_from_viewers": true } } }, { "cell_type": "code", "source": [ "/**\n", " * Matching by type\n", " */\n", "fun checkType(arg: Any): Unit = when(arg){\n", " is String -> println(\"I am a String. Length is ${arg.length}\")\n", " is Int -> println(\"I am an Int.\")\n", " is Double -> println(\"I am a Double\")\n", " // 2==2 -> println(\"Wat?\")\n", " else -> println(\"I don't know who am I?\")\n", "}\n", "\n", "fun checkType2(arg: Any): Unit = when{\n", " arg is String -> println(\"I am a String\")\n", " arg is Int -> println(\"I am an Int\")\n", " arg is Double -> println(\"I am a Double\")\n", " 2==2 -> println(\"Wat?\")\n", " else -> println(\"I don't know who am I?\")\n", "}\n", "\n", "checkType(true)" ], "metadata": { "datalore": { "node_id": "tJXxwCbGkVrfybQ0WZBOtt", "type": "CODE", "hide_input_from_viewers": true, "hide_output_from_viewers": true } }, "outputs": [], "execution_count": null }, { "cell_type": "markdown", "source": [ "## Try-catch expression\n", "https://kotlinlang.org/docs/idioms.html#try-catch-expression" ], "attachments": {}, "metadata": { "datalore": { "node_id": "f0bGwDoiIdt91b3tSeL1rn", "type": "MD", "hide_input_from_viewers": true, "hide_output_from_viewers": true } } }, { "cell_type": "code", "source": [ "fun tryCatch() {\n", " val result = try {\n", " 22\n", " } catch (e: ArithmeticException) {\n", " throw IllegalStateException(e)\n", " } finally{\n", " println(\"finally\")\n", " }\n", "\n", " // Working with result\n", "}" ], "metadata": { "datalore": { "node_id": "qT7cCbqiG1c4Agyc9qD5I3", "type": "CODE", "hide_input_from_viewers": true, "hide_output_from_viewers": true } }, "outputs": [], "execution_count": null }, { "cell_type": "code", "source": [ "\n", "val res: Result = runCatching { \n", " //error(\"Error happened\")\n", " 4\n", "}\n", "\n", "(res.getOrNull() ?: 0) + 1" ], "metadata": { "datalore": { "node_id": "MslvYANHimu0ByBevtI4NY", "type": "CODE", "hide_input_from_viewers": true, "hide_output_from_viewers": true } }, "outputs": [], "execution_count": null }, { "cell_type": "code", "source": [ "val dataFlow = sequence{\n", " var i = 0\n", " while(true){\n", " yield(i++)\n", " }\n", "}\n", "\n", "dataFlow.map { \n", " runCatching { \n", " if(it%2 ==0) error(\"Even: $it\") else it\n", " }\n", "}.take(10).toList()" ], "metadata": { "datalore": { "node_id": "4B3Hw7znY3cEDxBmytUJCV", "type": "CODE", "hide_input_from_viewers": true, "hide_output_from_viewers": true } }, "outputs": [], "execution_count": null }, { "cell_type": "markdown", "source": [ "## Nothing" ], "attachments": {}, "metadata": { "datalore": { "node_id": "cXeTiH13nXoPhTXZLkRnLj", "type": "MD", "hide_input_from_viewers": true, "hide_output_from_viewers": true } } }, { "cell_type": "code", "source": [ "/**\n", " * The declaration if valid because [TODO] returns Nothing\n", " */\n", "fun getSomething(): Int? = TODO()\n", "\n", "fun findSomething(): Int {\n", " // early return is valid because `return` operator result is Nothing\n", " val found: Int = getSomething() ?: return 2\n", " return found + 2\n", "}\n", "\n", "fun checkCondition(): Int {\n", " fun conditionSatisfied() = false\n", "\n", " return if (conditionSatisfied()) {\n", " 1\n", " } else {\n", " //error is Nothing\n", " error(\"Condition is not satisfied\")\n", " }\n", "}" ], "metadata": { "datalore": { "node_id": "h11q8FWGKYIcuFOnFOLmBl", "type": "CODE", "hide_input_from_viewers": true, "hide_output_from_viewers": true } }, "outputs": [], "execution_count": null }, { "cell_type": "markdown", "source": [ "## Data class" ], "attachments": {}, "metadata": { "datalore": { "node_id": "vOQiEaU7CW3kIB3K4W2hEK", "type": "MD", "hide_input_from_viewers": true, "hide_output_from_viewers": true } } }, { "cell_type": "code", "source": [ "\n", "class SimpleClass(val a: Int, val b: Double, c: Double){\n", " override fun toString() = \"SimpleClass(a=$a, b=$b)\"\n", "}\n", "\n", "\n", "data class DataClass(val a: Int, val b: Double) {\n", " val c get() = b + 1\n", "}\n", "\n", "val simple = SimpleClass(1, 2.0, 3.0)\n", "\n", "println(simple)\n", "\n", "val data = DataClass(2, 2.0)\n", "val copy = data.copy(b = 22.2)\n", "println(copy)" ], "metadata": { "datalore": { "node_id": "B7unRz7w7U8OhXejJeFspj", "type": "CODE", "hide_input_from_viewers": true, "hide_output_from_viewers": true } }, "outputs": [], "execution_count": null }, { "cell_type": "markdown", "source": [ "## Nullable truth" ], "attachments": {}, "metadata": { "datalore": { "node_id": "clBRddjfvKnMe2EMSmDhLd", "type": "MD", "hide_input_from_viewers": true, "hide_output_from_viewers": true } } }, { "cell_type": "code", "source": [ "\n", "/**\n", " * Nullable truth.\n", " */\n", "fun nullableTruth() {\n", " class A(val b: Boolean?)\n", "\n", " val a: A? = A(null) // Compilator warning here\n", " //use\n", " a?.b == true\n", " //instead of\n", " a?.b ?: false\n", "\n", " // The old way\n", " val res = if (a == null) {\n", " false\n", " } else {\n", " if (a.b == null) {\n", " false\n", " } else {\n", " a.b\n", " }\n", " }\n", "}" ], "metadata": { "datalore": { "node_id": "5BN3u1gHkSamtLIJiQicsV", "type": "CODE", "hide_input_from_viewers": true, "hide_output_from_viewers": true } }, "outputs": [], "execution_count": null }, { "cell_type": "markdown", "source": [ "## Safe call" ], "attachments": {}, "metadata": { "datalore": { "node_id": "5wCQZq5qasoVCldxVSxxBK", "type": "MD", "hide_input_from_viewers": true, "hide_output_from_viewers": true } } }, { "cell_type": "code", "source": [ "\n", "/**\n", " * Safe call and elvis operator\n", " */\n", "fun idiom12() {\n", " val files = File(\"Test\").listFiles()\n", " println(files?.size ?: \"empty\")\n", "}\n", "idiom12()" ], "metadata": { "datalore": { "node_id": "v4cP31zyrmBkHP7lnYZWN0", "type": "CODE", "hide_input_from_viewers": true, "hide_output_from_viewers": true } }, "outputs": [], "execution_count": null }, { "cell_type": "code", "source": [ "fun printNotNull(any: Any) = println(any)\n", "\n", "// printNotNull(null) does not work\n", "\n", "val value: Int? = 2\n", "//val value: Int? by lazy{ 2 }\n", "\n", "//printNotNull(value) // Error\n", "if (value != null) {\n", " //not guaranteed to work with mutable variable\n", " printNotNull(value)\n", "}\n", "\n", "var value1: Int? = 2\n", "\n", "// Safe call here\n", "value1?.let {\n", " printNotNull(it) // execute this block if not null\n", " //printNotNull(value1) // value is not null here\n", "}" ], "metadata": { "datalore": { "node_id": "hGZ9h4H8TaYZefCkAmP3Gd", "type": "CODE", "hide_input_from_viewers": true, "hide_output_from_viewers": true } }, "outputs": [], "execution_count": null }, { "cell_type": "markdown", "source": [ "## Nullable assignment" ], "attachments": {}, "metadata": { "datalore": { "node_id": "hzXkbfAThQ4cKtdGYuLejZ", "type": "MD", "hide_input_from_viewers": true, "hide_output_from_viewers": true } } }, { "cell_type": "code", "source": [ "\n", "/**\n", " * Dart-like (?=) nullable assignment\n", " */\n", "fun idiom11() {\n", " var x: String? = null\n", " x = x ?: \"Hello\"\n", "}" ], "metadata": { "datalore": { "node_id": "di2EXkn35CFEja9KMK1Ur5", "type": "CODE", "hide_input_from_viewers": true, "hide_output_from_viewers": true } }, "outputs": [], "execution_count": null }, { "cell_type": "markdown", "source": [ "## Extension functions" ], "attachments": {}, "metadata": { "datalore": { "node_id": "aC24azwiFpZfA2v3umRb7p", "type": "MD", "hide_input_from_viewers": true, "hide_output_from_viewers": true } } }, { "cell_type": "code", "source": [ "/**\n", " * Extension function on a String. It is not monkey-patching.\n", " */\n", "fun String.countOs(): Int = count { it == 'ы' } // implicit this points to String\n", "\n", "fun Int.printMe() = println(this) // explicit this\n", "\n", "fun Any?.doSomething(): Int = TODO(\"Don't do that\")\n", "\n", "fun ((Int) -> Int).evalAndIncrement(): (Int) -> Int = { this.invoke(it) + 1 }\n", "\n", "// val intFunction: (Int)->Int = {it -1}\n", "\n", "// val modifiedFunction = intFunction.evalAndIncrement()\n", "\n", "// println(modifiedFunction(0))\n", "\n", "\"вылысыпыдыстычка\".countOs().printMe()" ], "metadata": { "datalore": { "node_id": "li65OumXRClbEIwURW9DEE", "type": "CODE", "hide_input_from_viewers": true, "hide_output_from_viewers": true } }, "outputs": [], "execution_count": null }, { "cell_type": "code", "source": [ "infix fun > ClosedRange.intersect(other: ClosedRange): ClosedRange?{\n", " val start = maxOf(this.start, other.start)\n", " val end = minOf(this.endInclusive, other.endInclusive)\n", " return if(end>=start) start..end else null\n", "}\n", "\n", "(0..8).intersect(6..12)\n", "0..8 intersect 6..12" ], "metadata": { "datalore": { "node_id": "cyD7D8tmZJex8pJ3NsufHN", "type": "CODE", "hide_input_from_viewers": true, "hide_output_from_viewers": true } }, "outputs": [], "execution_count": null }, { "cell_type": "code", "source": [ "fun List.concat() = joinToString(separator = \"\")\n", "listOf(\"a\",\"b\",\"c\").concat()\n", "listOf(1,2,3).concat()" ], "metadata": { "datalore": { "node_id": "tnCskYd7474gKZiYnPHlx4", "type": "CODE", "hide_input_from_viewers": true, "hide_output_from_viewers": true } }, "outputs": [], "execution_count": null }, { "cell_type": "markdown", "source": [ "## Extension properties\n", "\n", "Remember [property usage guidelines](https://kotlinlang.org/docs/coding-conventions.html#functions-vs-properties)." ], "attachments": {}, "metadata": { "datalore": { "node_id": "pJTxzy4MfR710OowVbvf6s", "type": "MD", "hide_input_from_viewers": true, "hide_output_from_viewers": true } } }, { "cell_type": "code", "source": [ "\n", "/**\n", " * Extension property (must be virtual)\n", " */\n", "val List.odd get() = filter { it.toInt() % 2 == 1 }\n", "\n", "List(10){it}.odd" ], "metadata": { "datalore": { "node_id": "L7qpVNVtW0Fh6dMX4pR3il", "type": "CODE", "hide_input_from_viewers": true, "hide_output_from_viewers": true } }, "outputs": [], "execution_count": null }, { "cell_type": "code", "source": [ "var MutableMap.a: String?\n", " get() = this.get(\"a\")\n", " set(value){\n", " if(value == null){\n", " this.remove(\"a\")\n", " } else {\n", " this.set(\"a\",value)\n", " }\n", " }\n", "\n", "val map = mutableMapOf(\"a\" to \"a\", \"b\" to \"b\")\n", "\n", "map.a = \"6\"\n", "\n", "map.a" ], "metadata": { "datalore": { "node_id": "9DG1gyyYvzjbg0EZFfP5Qo", "type": "CODE", "hide_input_from_viewers": true, "hide_output_from_viewers": true } }, "outputs": [], "execution_count": null }, { "cell_type": "code", "source": [ "\n", "/**\n", " * Extension variable (also must be virtual)\n", " */\n", "var Array.second: Int\n", " get() = this[1]\n", " set(value) {\n", " this[1] = value\n", " }\n", "\n", "\n", "val array = Array(5){it}\n", "array.second = 9\n", "array" ], "metadata": { "datalore": { "node_id": "hAjUSxhPqTgmnz9WQYXg3U", "type": "CODE", "hide_input_from_viewers": true, "hide_output_from_viewers": true } }, "outputs": [], "execution_count": null }, { "cell_type": "markdown", "source": [ "## Scope functions (run, with, let)" ], "attachments": {}, "metadata": { "datalore": { "node_id": "Ziq72FmnnYUYPMy4CamsQ0", "type": "MD", "hide_input_from_viewers": true, "hide_output_from_viewers": true } } }, { "cell_type": "code", "source": [ "object AClass{\n", " val a = \"a\"\n", " val b = \"b\"\n", " val c = \"c\"\n", "}\n", "\n", "fun getAClass(): AClass? = null\n", "\n", "/**\n", " * [with]/[run]/[let] are used to create a scope with given argument or receiver\n", " */\n", "\n", "\n", "// Simple print of AClass\n", "println(\"a = ${AClass.a}, b = ${AClass.b}, c = ${AClass.c}\")\n", "\n", "// Using `with`\n", "val res = with(AClass){\n", " // AClass type is the receiver in this scope\n", " println(\"a = $a, b = $b, c = $c\")\n", " /*return@with*/ \"some value\"\n", "}\n", "\n", "res" ], "metadata": { "datalore": { "node_id": "7wtXhm3FeLejbPp7NEWiz2", "type": "CODE", "hide_input_from_viewers": true, "hide_output_from_viewers": true } }, "outputs": [], "execution_count": null }, { "cell_type": "code", "source": [ "\n", "//using `run`\n", "getAClass()?.run {\n", " // the same as above\n", " println(\"a = $a, b= $b, c = $c\")\n", " 2\n", "}\n", "\n", "//Using `let` to compose result. Not recommended using without a need\n", "val letResult = getAClass()?.let { arg ->\n", " arg.c + arg.a\n", "}\n" ], "metadata": { "datalore": { "node_id": "aJGJ3ukmhCchnZwJEzLV3K", "type": "CODE", "hide_input_from_viewers": true, "hide_output_from_viewers": true } }, "outputs": [], "execution_count": null }, { "cell_type": "markdown", "source": [ "## Scope functions (also, apply)" ], "attachments": {}, "metadata": { "datalore": { "node_id": "r6VfHIkDTBDCIotqNCslOH", "type": "MD", "hide_input_from_viewers": true, "hide_output_from_viewers": true } } }, { "cell_type": "code", "source": [ "/**\n", " * Using apply/also to add a finalizing action\n", " */\n", "var i = 2\n", "\n", "/**\n", "* [also] block does not affect the result\n", "*/\n", "fun getAndIncrement() = i.also { i += 1 } //don't do that\n", "\n", "println(getAndIncrement())\n", "println(i)" ], "metadata": { "datalore": { "node_id": "c0kY56DJne2lbwGShOgur6", "type": "CODE", "hide_input_from_viewers": true, "hide_output_from_viewers": true } }, "outputs": [], "execution_count": null }, { "cell_type": "code", "source": [ "\n", "class Rectangle {\n", " var length: Number = 0\n", " var breadth: Number = 0\n", " var color: Int = 0xffffff\n", "}\n", "\n", "/**\n", " * Configure properties of an object (apply)\n", " * https://kotlinlang.org/docs/idioms.html#configure-properties-of-an-object-apply\n", " */\n", "val myRectangle = Rectangle().apply {\n", " length = 4\n", " breadth = 5\n", " color = 0xFAFAFA\n", "}\n", "\n", "fun Rectangle(block: Rectangle.() -> Unit): Rectangle = Rectangle().apply(block)\n", "\n", "val myRectangle2 = Rectangle {\n", " length = 4\n", " breadth = 5\n", " color = 0xFAFAFA\n", "}\n", "\n", "fun Rectangle(length: Number) = Rectangle().apply { this.length = length }\n", "\n", "Rectangle(8)" ], "metadata": { "datalore": { "node_id": "m9HH2IzTy1dbYkBRUeFjh9", "type": "CODE", "hide_input_from_viewers": true, "hide_output_from_viewers": true } }, "outputs": [], "execution_count": null }, { "cell_type": "markdown", "source": [ "## Lists\n", "\n", "Do not cast List to MutableList!" ], "attachments": {}, "metadata": { "datalore": { "node_id": "lMgnCCxkRhs62cgTQN5eDK", "type": "MD", "hide_input_from_viewers": true, "hide_output_from_viewers": true } } }, { "cell_type": "code", "source": [ "/**\n", " * Lists and mutable lists\n", " */\n", "\n", "\n", "/**\n", " * This method creates a read-only list of strings. One should note that the type of the list is not specified\n", " */\n", "val list = listOf(\"a\", \"b\", \"c\")\n", "\n", "println(list[0])\n", "println(list.get(1))\n", "//println(list.get(4))\n", "println(list.getOrNull(4) ?: \"nothing\")" ], "metadata": { "datalore": { "node_id": "pP9PTSy6IXeTC6HITFtBoR", "type": "CODE", "hide_input_from_viewers": true, "hide_output_from_viewers": true } }, "outputs": [], "execution_count": null }, { "cell_type": "code", "source": [ "\n", "/**\n", "* This one creates a mutable list\n", "*/\n", "val mutableList: MutableList = mutableListOf(\"a\", \"b\", \"c\")\n", "mutableList[2] = \"d\"\n", "mutableList.add(\"e\")\n", "mutableList += \"f\"\n", "mutableList" ], "metadata": { "datalore": { "node_id": "Kx565m7VjuF2RncqW7gw2i", "type": "CODE", "hide_input_from_viewers": true, "hide_output_from_viewers": true } }, "outputs": [], "execution_count": null }, { "cell_type": "code", "source": [ "listOf(\"a\")::class" ], "metadata": { "datalore": { "node_id": "zKxwfvdRQwsgqRxK5GPTIK", "type": "CODE", "hide_input_from_viewers": true, "hide_output_from_viewers": true } }, "outputs": [], "execution_count": null }, { "cell_type": "code", "source": [ "var roList = listOf(\"a\", \"b\")\n", "roList += setOf(\"c\")\n", "roList" ], "metadata": { "datalore": { "node_id": "yK6QD0dvrT98t7dx0pWJ5s", "type": "CODE", "hide_input_from_viewers": true, "hide_output_from_viewers": true } }, "outputs": [], "execution_count": null }, { "cell_type": "code", "source": [ "\n", "/**\n", " * This one creates a mutable ArrayList.\n", " */\n", "val arrayList: ArrayList = arrayListOf(\"a\", \"b\", \"c\")\n", "\n", "//Danger zone\n", "\n", "val newList: List = list + \"f\" + mutableList\n", "\n", "println(newList)" ], "metadata": { "datalore": { "node_id": "qSaaZGyHiH9dsL4dtQ8UyE", "type": "CODE", "hide_input_from_viewers": true, "hide_output_from_viewers": true } }, "outputs": [], "execution_count": null }, { "cell_type": "code", "source": [ "\n", "//Bonus\n", "\n", "val lambdaList = List(3){ it.toString() }\n", "println(lambdaList)\n", "\n", "\n", "val builderList: List = buildList{\n", " add(2)\n", " add(8)\n", " remove(8)\n", "}\n", "builderList" ], "metadata": { "datalore": { "node_id": "JPSYaCAgOZRveNdFPO15SG", "type": "CODE", "hide_input_from_viewers": true, "hide_output_from_viewers": true } }, "outputs": [], "execution_count": null }, { "cell_type": "code", "source": [ "// val sequentialList = List(20){it}\n", "// sequentialList.zipWithNext{ l, r -> r - l }.forEach{println(it)}" ], "metadata": { "datalore": { "node_id": "fQBhhTBHCdc9JkWiMZ6uEh", "type": "CODE", "hide_input_from_viewers": true, "hide_output_from_viewers": true } }, "outputs": [], "execution_count": null }, { "cell_type": "markdown", "source": [ "## Shortcut collection builders" ], "attachments": {}, "metadata": { "datalore": { "node_id": "rePWgKfjyysV69ubD4fWAR", "type": "MD", "hide_input_from_viewers": true, "hide_output_from_viewers": true } } }, { "cell_type": "code", "source": [ "/**\n", " * Use shortcut function to provide default values\n", " */\n", "fun doSomething(additionalArguments: List = emptyList()){\n", " TODO()\n", " emptyArray()\n", " emptySet()\n", " emptyMap()\n", "}\n", "emptyList()::class" ], "metadata": { "datalore": { "node_id": "F5RQ5108YJe3pzfN0eAWwm", "type": "CODE", "hide_input_from_viewers": true, "hide_output_from_viewers": true } }, "outputs": [], "execution_count": null }, { "cell_type": "markdown", "source": [ "## Maps" ], "attachments": {}, "metadata": { "datalore": { "node_id": "TSOQN81tDBKM3Z3oUOHGom", "type": "MD", "hide_input_from_viewers": true, "hide_output_from_viewers": true } } }, { "cell_type": "code", "source": [ "val map = mutableMapOf(\n", " \"key\" to \"a\",\n", " \"key2\" to \"b\",\n", ")\n", "\n", "//The map could be accessed via indexing operation\n", "println(map[\"key\"])\n", "map[\"key\"] = \"fff\"\n", "println(map[\"key\"])\n", "println(map[\"key3\"])" ], "metadata": { "datalore": { "node_id": "cZd2JPB72uAfnx1o8AYjOs", "type": "CODE", "hide_input_from_viewers": true, "hide_output_from_viewers": true } }, "outputs": [], "execution_count": null }, { "cell_type": "code", "source": [ "\n", "//val entry: MutableMap.MutableEntry = map.iterator().next()\n", "\n", "/**\n", " * The destructuring declaration for maps and other objects that support `operator fun componentN`\n", " */\n", "for ((k, v) in map) {\n", "//val (k, v) = entry\n", "// val k = entry.component1()\n", "// val v = entry.component2()\n", " println(\"$k -> $v\")\n", "}" ], "metadata": { "datalore": { "node_id": "hRuaLFdpTsdnt7lYKL2nbN", "type": "CODE", "hide_input_from_viewers": true, "hide_output_from_viewers": true } }, "outputs": [], "execution_count": null }, { "cell_type": "code", "source": [ "map.forEach { (k, v) -> println(\"$k -> $v\")}" ], "metadata": { "datalore": { "node_id": "s1Q65aNWb9YNBZOgxmjotW", "type": "CODE", "hide_input_from_viewers": true, "hide_output_from_viewers": true } }, "outputs": [], "execution_count": null }, { "cell_type": "code", "source": [ "\n", "val (a, b) = Pair(1, 2)\n", "\n", "val coord = doubleArrayOf(0.0, 1.0, 2.0)\n", "\n", "val (x,y,z) = coord\n", "\n", "data class Coordinate(val x: Double, val y: Int)\n", "\n", "val (x1, y1) = Coordinate(1.0,2)" ], "metadata": { "datalore": { "node_id": "t8YITxVsoTt7o9KnnOvO0Z", "type": "CODE", "hide_input_from_viewers": true, "hide_output_from_viewers": true } }, "outputs": [], "execution_count": null }, { "cell_type": "markdown", "source": [ "## Mutable type access decorator\n", "\n", "To be replaced with qualified getter types soon" ], "attachments": {}, "metadata": { "datalore": { "node_id": "Ec2mbBaydQnVsjaiThy2rn", "type": "MD", "hide_input_from_viewers": true, "hide_output_from_viewers": true } } }, { "cell_type": "code", "source": [ "class AClassWithList{\n", " var b: Double = 2.0\n", " private set\n", "\n", " init{\n", " b = 5.0\n", " }\n", "\n", " private val _list: MutableList = ArrayList()\n", " val list: List get() = _list\n", "}\n", "\n", "\n", "\n", "val obj = AClassWithList()\n", "\n", "// obj.b = 10.0 //error\n", "obj.list.add(43)" ], "metadata": { "datalore": { "node_id": "H8VTPpGlV0VwsHL4Q8lJF9", "type": "CODE", "hide_input_from_viewers": true, "hide_output_from_viewers": true } }, "outputs": [], "execution_count": null }, { "cell_type": "markdown", "source": [ "## Contains operator and ranges" ], "attachments": {}, "metadata": { "datalore": { "node_id": "GKoCVcgdnSYPjsNCAJIkBB", "type": "MD", "hide_input_from_viewers": true, "hide_output_from_viewers": true } } }, { "cell_type": "code", "source": [ "\n", "val emailsList = emptyList()\n", "\n", "// When used directly infix in operator checks if the element is contained in a collection\n", "//using `operator fun contains`\n", "\n", "if (\"john@example.com\" in emailsList) {\n", " println(\"is in list\")\n", "}\n", "\n", "if (\"jane@example.com\" !in emailsList) {\n", " println(\"not in list\")\n", "}" ], "metadata": { "datalore": { "node_id": "4mh1Lg6g8OWEIC1Qjgp9tN", "type": "CODE", "hide_input_from_viewers": true, "hide_output_from_viewers": true } }, "outputs": [], "execution_count": null }, { "cell_type": "code", "source": [ "\n", "\n", "class DateRange(val start: Instant, val end: Instant)\n", "\n", "operator fun DateRange.contains(value: Instant): Boolean = value > start && value < end\n", "\n", "\n", "println(Instant.now() in DateRange(Instant.EPOCH, Instant.MAX))" ], "metadata": { "datalore": { "node_id": "1N4A00CAksn2nVg57DRKnp", "type": "CODE", "hide_input_from_viewers": true, "hide_output_from_viewers": true } }, "outputs": [], "execution_count": null }, { "cell_type": "code", "source": [ "\n", "// Another (different) use of `in` is iteration over range or collection using\n", "// using `operator fun iterator`\n", "\n", "for (i in 1..100) {\n", " println(i)\n", "} // closed range: includes 100\n", "\n", "(1..100).forEach { i ->\n", " println(i)\n", "} //the same, but with boxing\n", "\n", "for (i in 1..<100) {\n", " println(i)\n", "} // half-open range: does not include 100\n", "\n", "for (x in 2..10 step 2) {\n", " println(x)\n", "}\n", "for (x in 10 downTo 1) {\n", " println(x)\n", "}\n", "\n", "infix fun ClosedRange.step(step: Double): Sequence {\n", " //TODO check arguments\n", " var current = start\n", " return sequence {\n", " do {\n", " yield(current)\n", " current += step\n", " } while (current <= endInclusive)\n", " }\n", "}\n", "\n", "for (x in 0.0..10.0 step 0.5){\n", " println(x)\n", "}" ], "metadata": { "datalore": { "node_id": "hwJfUD7H5mDtjStAsGppk7", "type": "CODE", "hide_input_from_viewers": true, "hide_output_from_viewers": true } }, "outputs": [], "execution_count": null }, { "cell_type": "markdown", "source": [ "## Map-reduce" ], "attachments": {}, "metadata": { "datalore": { "node_id": "IsJiqd43JpY7BnZTeHNU4M", "type": "MD", "hide_input_from_viewers": true, "hide_output_from_viewers": true } } }, { "cell_type": "code", "source": [ "val list: List = listOf(1, 2, 3, 4, 5, 6)\n", "\n", "val result = list\n", " //.stream().parallel()\n", " //.asSequence()\n", " .filter { it % 2 == 0 } //select even numbers\n", " .map { it * it } // get square of each element\n", " //.onEach { println(it) }\n", " .sumOf { it } //use one of reduce operations\n", " //.reduce { acc: Int, i: Int -> acc + i }\n", "\n", "result" ], "metadata": { "datalore": { "node_id": "KKT62bidVYF8I147heptAS", "type": "CODE", "hide_input_from_viewers": true, "hide_output_from_viewers": true } }, "outputs": [], "execution_count": null }, { "cell_type": "code", "source": [ "val sequence = sequence {\n", " var counter = 1\n", " while(true){\n", " yield(counter++)\n", " yield(counter++)\n", " // println(counter)\n", " }\n", "}\n", "\n", "val result = sequence\n", " .take(6)\n", " .filter { it % 2 == 0 } //select even numbers\n", " .map { it * it } // get square of each element\n", " //.onEach { println(it) }\n", " //.sumOf { it } //use one of reduce operations\n", " .reduce { acc: Int, i: Int -> acc + i }\n", "\n", "result" ], "metadata": { "datalore": { "node_id": "9qcw7ejFSF8QnAGdf1bAI0", "type": "CODE", "hide_input_from_viewers": true, "hide_output_from_viewers": true } }, "outputs": [], "execution_count": null }, { "cell_type": "markdown", "source": [ "## Wrap mutable logic" ], "attachments": {}, "metadata": { "datalore": { "node_id": "k1eHOcIZbnHDJ8g7ZusArx", "type": "MD", "hide_input_from_viewers": true, "hide_output_from_viewers": true } } }, { "cell_type": "code", "source": [ "val list = buildList {\n", " repeat(10){\n", " add(it)\n", " }\n", "}\n", "println(list)\n" ], "metadata": { "datalore": { "node_id": "pJfVynao7enb7Q07lJRGRl", "type": "CODE", "hide_input_from_viewers": true, "hide_output_from_viewers": true } }, "outputs": [], "execution_count": null }, { "cell_type": "markdown", "source": [ "## Scoped resource usage" ], "attachments": {}, "metadata": { "datalore": { "node_id": "ovz77VglMntuRrJ1uMtSEG", "type": "MD", "hide_input_from_viewers": true, "hide_output_from_viewers": true } } }, { "cell_type": "code", "source": [ "val stream = Files.newInputStream(Paths.get(\"file.txt\"))\n", "// The resource is automatically closed when leaving the scope\n", "stream.bufferedReader().use { reader ->\n", " println(reader.readText())\n", "}" ], "metadata": { "datalore": { "node_id": "XIX7lDFJ0UiyiJAY4c3Oby", "type": "CODE", "hide_input_from_viewers": true, "hide_output_from_viewers": true } }, "outputs": [], "execution_count": null }, { "cell_type": "markdown", "source": [ "## Factory as parameter and companion factories" ], "attachments": {}, "metadata": { "datalore": { "node_id": "MizpHzkgkV5TzoM9HFS2zM", "type": "MD", "hide_input_from_viewers": true, "hide_output_from_viewers": true } } }, { "cell_type": "code", "source": [ "interface Factory {\n", " fun build(str: String): T\n", "}\n", "\n", "data class IntContainer(val arg: Int) {\n", " \n", " companion object : Factory {\n", " override fun build(str: String) = IntContainer(str.toInt())\n", "\n", " fun buildSpecial(str: String) = IntContainer(str.toInt())\n", " }\n", "}\n", "\n", "data class DoubleContainer(val arg: Double) {\n", " \n", " companion object : Factory {\n", " override fun build(str: String) = DoubleContainer(str.toDouble())\n", " }\n", "}\n", "\n", "fun buildContainer(str: String, factory: Factory): T = factory.build(str)\n", "\n", "\n", "buildContainer(\"22\", IntContainer)\n" ], "metadata": { "datalore": { "node_id": "TAnNAjBb2Rc1RRTTICVuZg", "type": "CODE", "hide_input_from_viewers": true, "hide_output_from_viewers": true } }, "outputs": [], "execution_count": null }, { "cell_type": "markdown", "source": [ "## Initialization" ], "attachments": {}, "metadata": { "datalore": { "node_id": "l8Zp9Da24jvCFJd28sDNFj", "type": "MD", "hide_input_from_viewers": true, "hide_output_from_viewers": true } } }, { "cell_type": "code", "source": [ "\n", "open class Bad{\n", " val value: Int\n", "\n", " init {\n", " value = requestValue()\n", " }\n", "\n", " open fun requestValue(): Int {\n", " doSomethingElse()\n", " return 2\n", " }\n", "\n", " private fun doSomethingElse(){\n", " println(value)\n", " }\n", "}\n", "\n", "Bad()" ], "metadata": { "datalore": { "node_id": "xZWJjxthdbhrVOMYfed6UV", "type": "CODE", "hide_input_from_viewers": true, "hide_output_from_viewers": true } }, "outputs": [], "execution_count": null }, { "cell_type": "code", "source": [ "\n", "\n", "//Factory functions are preferred to the initialization logic\n", "\n", "data class Good internal constructor(val value: Int){\n", " init {\n", " //Initialization block is there to check arguments\n", " require(value >= 0)\n", " }\n", "\n", " companion object\n", "}\n", "\n", "fun requestValue(): Int = TODO()\n", "\n", "// This is the factory-function\n", "fun Good() = Good(requestValue())\n", "\n", "\n", "// additional constructor-like builders could be added to the companion\n", "\n", "@OptIn(ExperimentalUnsignedTypes::class)\n", "fun Good.Companion.build(value: UInt) = Good(value.toInt())\n", "\n", "Good.build(32U)" ], "metadata": { "datalore": { "node_id": "2oRxs51NNlsoJD7UIx3exC", "type": "CODE", "hide_input_from_viewers": true, "hide_output_from_viewers": true } }, "outputs": [], "execution_count": null }, { "cell_type": "markdown", "source": [ "## Delegates" ], "attachments": {}, "metadata": { "datalore": { "node_id": "jvl9An4EkBkWd9B1JX4ju9", "type": "MD", "hide_input_from_viewers": true, "hide_output_from_viewers": true } } }, { "cell_type": "code", "source": [ "class ClassWithALazyProperty{\n", " //Use lazy delegate for something that should be calculated ones on first call\n", " val lazyValue by lazy {\n", " //Do dome heavy logic here\n", " println(\"Initialized\")\n", " 22\n", " }\n", "\n", " val getterValue: Int get(){\n", " println(\"got\")\n", " return 33\n", " }\n", "}\n", "val lazyClass = ClassWithALazyProperty()\n", "lazyClass.lazyValue\n", "lazyClass.lazyValue\n", "lazyClass.getterValue\n", "lazyClass.getterValue" ], "metadata": { "datalore": { "node_id": "CqN0VKctxRWd4N4RrKC90M", "type": "CODE", "hide_input_from_viewers": true, "hide_output_from_viewers": true } }, "outputs": [], "execution_count": null }, { "cell_type": "code", "source": [ "\n", "//Using other delegates\n", "val map = mutableMapOf(\"a\" to 1, \"b\" to 2)\n", "\n", "var a: Int by map\n", "\n", "println(a)\n", "a = 3\n", "println(map)" ], "metadata": { "datalore": { "node_id": "TYw8wCso8aGvqvd2XviRcH", "type": "CODE", "hide_input_from_viewers": true, "hide_output_from_viewers": true } }, "outputs": [], "execution_count": null }, { "cell_type": "markdown", "source": [ "## Inline functions" ], "attachments": {}, "metadata": { "datalore": { "node_id": "OkBtMlEkvfaglVXzlzq5dU", "type": "MD", "hide_input_from_viewers": true, "hide_output_from_viewers": true } } }, { "cell_type": "code", "source": [ "\n", "/**\n", " * Definition of inline function\n", " */\n", "inline fun List.forEachOdd(block: (Int) -> Unit) = forEach {\n", " if (it % 2 == 1) block(it)\n", "}\n", "\n", "\n", "/**\n", " * The demonstration of use of inline [forEach] function with non-local return\n", " */\n", "fun foo(): Int {\n", " listOf(1, 2, 3, 4, 5).forEach {\n", " if (it == 3) return it // non-local return directly to the caller of foo()\n", " print(\"$it, \")\n", " }\n", " println(\"this point is unreachable\")\n", " return 0\n", "}\n", "foo()" ], "metadata": { "datalore": { "node_id": "kz2Dq5cDNhfbfgI6mXv5yU", "type": "CODE", "hide_input_from_viewers": true, "hide_output_from_viewers": true } }, "outputs": [], "execution_count": null }, { "cell_type": "code", "source": [ "\n", "/**\n", " * Using inline function for type reification during the compile time\n", " */\n", "inline fun List.prettyPrint() = forEach {\n", " when (T::class) {\n", " Double::class -> println(\"Double: ${it as Double}\")\n", " Int::class -> println(\"Int: ${it as Int}\")\n", " else -> it.toString()\n", " }\n", "}\n", "\n", "inline fun prettyPrintOne(arg: T) = listOf(arg).prettyPrint()\n", "\n", "listOf(1, 2, 3).prettyPrint()\n", "\n", "/**\n", " * **WARNING** inline functions are an advanced feature and should be used only for\n", " * reification or non-local return\n", " * NOT for optimization.\n", " */" ], "metadata": { "datalore": { "node_id": "drMX3vZ5PBfpBXhCZM1zc5", "type": "CODE", "hide_input_from_viewers": true, "hide_output_from_viewers": true } }, "outputs": [], "execution_count": null }, { "cell_type": "markdown", "source": [ "## Collections and boxing" ], "attachments": {}, "metadata": { "datalore": { "node_id": "6I3SqcgBGWPqpKZRhNDfAM", "type": "MD", "hide_input_from_viewers": true, "hide_output_from_viewers": true } } }, { "cell_type": "code", "source": [ "/**\n", " * Values are boxed. Each call is indirect\n", " */\n", "val list: List = List(20) { it.toDouble() }\n", "\n", "/**\n", " * Still boxed\n", " */\n", "val genericArray: Array = Array(20) { it.toDouble() }\n", "\n", "/**\n", " * Non-boxed\n", " */\n", "val specializedArray: DoubleArray = DoubleArray(20) { it.toDouble() }" ], "metadata": { "datalore": { "node_id": "ieHZ3zzSufTtNWM11pIuh1", "type": "CODE", "hide_input_from_viewers": true, "hide_output_from_viewers": true } }, "outputs": [], "execution_count": null }, { "cell_type": "markdown", "source": [ "Вопросы для самоконтроля:\n", "\n", "1.\tВ какой момент происходит интерполяция строк? Во время компиляции или во время выполнения?\n", "2.\tВо что компилируются функции верхнего уровня?\n", "3.\tЧем отличается Unit от других объектов.\n", "4.\tКакой порядок разрешения функций со значениями по умолчанию.\n", "5.\tЧто не является выражением в Котлин?\n", "6.\tКто такой Волдеморт?\n", "7.\tВ чем отличие declaration site variance от use site variance. Какой из вариантов поддерживается в Котлин?\n", "8.\tКак выводится тип выражения try?\n", "9.\tЕсть ли разница между `Nothing` и `Nothing?` ?\n", "10.\tКакие ограничения наложены на конструктор data class.\n", "11.\tВ каком случае вычисляется правая часть выражения безопасного вызова?\n", "12.\tЧто такое Элвис оператор?\n", "13.\tКак работает стирание дженериков при разрешении расширений?\n", "14.\tМожет ли у свойства-расширения быть собственное значение?\n", "15.\tЧем отличается run от with?\n", "16.\tСтоит ли использовать scope функции везде, где это возможно.\n", "17.\tЧем отличается arrayListOf()` от `mutableListOf()`?\n", "18.\tList в Kotlin всегда реализует List в Java при интеропе?\n", "19.\tМожно ли сделать собствен карту get и set при помощи квадратных скобок?\n", "20.\tВ чем отличие forEach в стандартной библиотеке Котлин и Java?\n", "21.\tКак работают функции componentN?\n", "22.\tПочему в Котлин нет диапазонов для чисел с плавающей точкой?\n", "23.\tКакой оператор должен быть реализован типе для того, чтобы он стал делегатом?\n", "24.\tМожно ли делегировать при помощи расширения?\n", "25.\tВ какой момент происходит инлайнинг инлайн функции?\n", "26.\tМожет ли быть сделана реификация типа без инлайна?\n", "27.\tВ каких случаях следует использовать тип Array?" ], "attachments": {}, "metadata": { "datalore": { "node_id": "v6c4s0X9jSpc2ZDPAuXAz7", "type": "MD", "hide_input_from_viewers": true, "hide_output_from_viewers": true } } }, { "metadata": {}, "cell_type": "code", "source": "", "outputs": [], "execution_count": null } ], "metadata": { "kernelspec": { "display_name": "Kotlin", "language": "kotlin", "name": "kotlin" }, "datalore": { "computation_mode": "JUPYTER", "package_manager": "pip", "base_environment": "default", "packages": [], "report_row_ids": [ "xIF3Up82GVsiV1Ceze6Si3", "ypTKLK2KvAZfnMbTgaVqNK", "qyGLHGsLl6mRCaOjpZBTD3", "7tql3kbsQ9JIbbEfghi48B", "BQz86nEWOj77fxXQ8iS47U", "RTl3zV3tStzlLoIHSvEZiV", "xmBQdVRhRGLsw0t87vxQW7", "MpSiMp90k75zOtoELK5XSp", "vqa5BMfmP7oZwt8sdVLKi8" ], "report_link": "https://datalore.jetbrains.com/report/ptQDfQAcrjNxzIO0AEqovZ/VXj2xqMTETu24Qra9ydiLo", "version": 3 } }, "nbformat": 4, "nbformat_minor": 4 }