{ "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 \\$a\")", "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", " \"\"\"\n", " \\\\\\\n", " This is a \n", " multi\n", " line\n", " raw\n", " string\n", "\"\"\".trimIndent()\n", ")" ], "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 }, { "metadata": {}, "cell_type": "code", "source": [ "var _a = 2\n", "val a: Int get() = _a\n", "\n", "println(a)\n", "_a = 3\n", "println(a)" ], "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", " /**\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": [ "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 }, { "metadata": {}, "cell_type": "code", "source": [ "fun doNothing() {\n", "\n", "}\n", "\n", "val a = doNothing()\n", "a::class" ], "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(4)\n", "doSomethingWithDefault(2, \"aaa\")\n", "doSomethingWithDefault(b = \"fff\", a = 8)\n", "doSomethingWithDefault(6, 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, PI) //error\n", "integrate(0.0, step = 0.02, to = PI) { sin(it) }\n", "//integrate(0.0, step = 0.02, PI) { sin(it) }\n", "\n", "integrate(0.0, step = 0.02, function = { sin(it) }, to = PI)" ], "metadata": { "datalore": { "node_id": "LT2YprLmzrgVcyBo3GKilz", "type": "CODE", "hide_input_from_viewers": true, "hide_output_from_viewers": true } }, "outputs": [], "execution_count": null }, { "metadata": {}, "cell_type": "code", "source": [ "fun functionWithParameters() {\n", " println(\"without default\")\n", "}\n", "\n", "//@JvmOverloads\n", "@JvmName(\"functionWithDefaultParameters\")\n", "fun functionWithParameters(a: Int = 2) { // don't do that\n", " println(\"with default\")\n", "}\n", "\n", "functionWithParameters()" ], "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 BuildResult(defaultInt: Int, block: Builder.() -> Unit): BuildResult =\n", " Builder(int = defaultInt).apply(block).build()\n", "\n", "val res = BuildResult(2) {\n", " str = \"ff\"\n", " optional = 1.0\n", "}\n", "\n", "res" ], "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", "fun interface AnInterface {\n", " val a: Int\n", " get() = 4\n", "\n", " // set(value){\n", " // println(value)\n", " // }\n", " fun doSomething() //= println(\"From interface\")\n", "\n", " fun doSomething2() = Unit\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", " override fun doSomething2(): Unit = TODO(\"Not yet implemented\")\n", "}\n", "\n", "val anonymous2 = AnInterface {\n", " println(\"Do something\")\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 }, { "metadata": {}, "cell_type": "code", "source": [ "fun interface Float64Function : (Double) -> Double {\n", " override operator fun invoke(arg: Double): Double\n", "\n", " operator fun invoke(doubles: DoubleArray): DoubleArray = DoubleArray(doubles.size) { invoke(doubles[it]) }\n", "}\n", "\n", "val sin = object : Float64Function {\n", " override fun invoke(p1: Double): Double = sin(p1)\n", "}\n", "\n", "val cos = Float64Function { kotlin.math.cos(it) }\n", "\n", "val tg = Float64Function(::tan)\n", "\n", "sin(PI / 2)\n", "\n", "sin(doubleArrayOf(0.0, PI / 2))" ], "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", "}\n", "\n", "interface Doer : Producer, Consumer {\n", "\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 }, { "metadata": {}, "cell_type": "code", "source": "fun transformIf(flag: Boolean): Int = if (flag) 1 else 0 ", "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: Int = 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": [ "val res: Result = runCatching {\n", " //error(\"Error happened\")\n", " 4\n", "}\n", "\n", "println(res.exceptionOrNull())\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().joinToString(separator = \"\\n\")" ], "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", "//FIXME don't do that\n", "fun dontDoThat() {\n", " if (true) return\n", " println(false)\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 }, { "metadata": {}, "cell_type": "code", "source": [ "fun loop(block: () -> Unit): Nothing {\n", " while (true) {\n", " block()\n", " }\n", "}" ], "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": [ "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, var b: Double /*, d: Double */) {\n", " init {\n", " require(b >= 0) { \"B should be positive\" }\n", " }\n", "\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", " * 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 }, { "metadata": {}, "cell_type": "code", "source": [ "import java.util.Optional\n", "import kotlin.reflect.typeOf\n", "\n", "println(typeOf>() == typeOf>>())\n", "\n", "println(typeOf() == typeOf())" ], "outputs": [], "execution_count": null }, { "metadata": {}, "cell_type": "code", "source": "mapOf().get(\"f\")!!", "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": [ "import java.io.File\n", "\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", " * 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 }, { "metadata": {}, "cell_type": "code", "source": "0.0..8.0 intersect 2.9..6.1", "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 }, { "metadata": {}, "cell_type": "code", "source": "", "outputs": [], "execution_count": null }, { "metadata": {}, "cell_type": "code", "source": [ "val functionWithReciever: List.(arg: Int) -> Unit = { arg ->\n", " println(get(arg))\n", "}\n", "\n", "listOf(\"1\", \"2\", \"3\").functionWithReciever(1)" ], "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", " * 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(\n", " \"a\" to \"a\",\n", " \"b\" to \"b\"\n", ")\n", "val map2 = mutableMapOf(\n", " Pair(\"a\", \"a\"),\n", " Pair(\"b\", \"b\")\n", ")\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", " * 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 = ${this.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 }, { "metadata": {}, "cell_type": "code", "source": [ "object AContext {\n", " fun AClass.abc() = a + b + c // warning additional concatenation\n", "}\n", "\n", "fun printAbc(aClass: AClass) = with(AContext) {\n", " aClass.abc()\n", "}" ], "outputs": [], "execution_count": null }, { "cell_type": "code", "source": [ "//using `run`\n", "getAClass()?.takeIf { it.a.isNotEmpty() }?.run {\n", " // the same as above\n", " println(\"a = $a, b= $b, c = $c\")\n", " 2\n", "}\n", "\n", "val runResult: Int = run {\n", "\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 }, { "metadata": {}, "cell_type": "code", "source": [ "//Don't do that\n", "fun scopeAbuse(str: String?) =\n", " str.takeIf { it?.isNotEmpty() == true }?.let { it.substring(0..4) }?.let { it.matches(\".*\".toRegex()) }\n", "\n", "fun withoutAbuse(str: String?): Boolean? = if (str?.isNotEmpty() == true) {\n", " val substring = str.substring(0..4)\n", " substring.matches(\".*\".toRegex())\n", "} else {\n", " null\n", "}" ], "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", "fun incrementAndPring(arg: Int) = (arg + 1).also{\n", " println(it)\n", "}\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": [ "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 }, { "metadata": {}, "cell_type": "code", "source": "", "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: 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\")\n", "list::class" ], "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", " * 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 }, { "metadata": {}, "cell_type": "code", "source": [ "//don't do that ever\n", "fun doBadThingWithList(list: List): List = (list as MutableList).apply { add(\"something\") }\n", "\n", "doBadThingWithList(listOf(\"a\", \"b\", \"c\"))" ], "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 }, { "metadata": {}, "cell_type": "code", "source": [ "val mutableList = mutableListOf(1, 2, 3)\n", "\n", "fun consumeList(list: List) {\n", " println(list.joinToString())\n", "}\n", "consumeList(mutableList)\n", "mutableList.add(4)\n", "consumeList(mutableList)" ], "outputs": [], "execution_count": null }, { "metadata": {}, "cell_type": "code", "source": "listOf(1, 2) + listOf(3, 4)", "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 }, { "metadata": {}, "cell_type": "code", "source": [ "val writableList = mutableListOf(\"a\", \"b\") // Warning!\n", "writableList += setOf(\"c\")\n", "writableList" ], "outputs": [], "execution_count": null }, { "cell_type": "code", "source": [ "/**\n", " * This one creates a mutable ArrayList.\n", " */\n", "val arrayList: ArrayList = arrayListOf(\"a\", \"b\", \"c\")\n", "\n", "//ArrayList()\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": [ "//Bonus\n", "\n", "val lambdaList = List(3) { it.toString() }\n", "println(lambdaList)\n", "\n", "val builderList: List = buildList {\n", " add(2)\n", " add(8)\n", " remove(8)\n", "}\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 }, { "metadata": {}, "cell_type": "code", "source": "", "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::class)\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": [ "//val entry: MutableMap.MutableEntry = map.iterator().next()\n", "\n", "//map.entries.first().component2()\n", "\n", "/**\n", " * The destructuring declaration for maps and other objects that support `operator fun componentN`\n", " */\n", "for ((k: String, 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\") }\n", "\n", "// java version\n", "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": [ "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", " fun add(int: Int) {\n", " require(int > 0)\n", " _list.add(int)\n", " }\n", "}\n", "\n", "\n", "val obj = AClassWithList()\n", "\n", "// obj.b = 10.0 //error\n", "obj.add(42)\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": "## Wrap mutable logic / Kotlin builder pattern", "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)" ], "metadata": { "datalore": { "node_id": "pJfVynao7enb7Q07lJRGRl", "type": "CODE", "hide_input_from_viewers": true, "hide_output_from_viewers": true } }, "outputs": [], "execution_count": null }, { "metadata": {}, "cell_type": "code", "source": [ "data class ImmutableObject(val a: String, val b: String, val c: Int)\n", "\n", "class ImmutableObjectBuilder(a: String) {\n", " var a: String = a\n", " var b: String = \"\"\n", " var c = 0\n", "\n", " fun build() = ImmutableObject(a, b, c)\n", "}\n", "\n", "/*inline*/ fun ImmutableObject(a: String, block: ImmutableObjectBuilder.() -> Unit): ImmutableObject =\n", " ImmutableObjectBuilder(a).apply(block).build()\n", "\n", "ImmutableObject(\"aValue\") {\n", " c = 99\n", "}" ], "outputs": [], "execution_count": null }, { "metadata": { "datalore": { "node_id": "GKoCVcgdnSYPjsNCAJIkBB", "type": "MD", "hide_input_from_viewers": true, "hide_output_from_viewers": true } }, "cell_type": "markdown", "source": "## Contains operator and ranges" }, { "metadata": { "datalore": { "node_id": "4mh1Lg6g8OWEIC1Qjgp9tN", "type": "CODE", "hide_input_from_viewers": true, "hide_output_from_viewers": true } }, "cell_type": "code", "source": [ "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", "}" ], "outputs": [], "execution_count": null }, { "metadata": { "datalore": { "node_id": "1N4A00CAksn2nVg57DRKnp", "type": "CODE", "hide_input_from_viewers": true, "hide_output_from_viewers": true } }, "cell_type": "code", "source": [ "import java.time.*\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))" ], "outputs": [], "execution_count": null }, { "metadata": { "datalore": { "node_id": "hwJfUD7H5mDtjStAsGppk7", "type": "CODE", "hide_input_from_viewers": true, "hide_output_from_viewers": true } }, "cell_type": "code", "source": [ "// 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", "\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", "}" ], "outputs": [], "execution_count": null }, { "metadata": { "datalore": { "node_id": "IsJiqd43JpY7BnZTeHNU4M", "type": "MD", "hide_input_from_viewers": true, "hide_output_from_viewers": true } }, "cell_type": "markdown", "source": "## Map-reduce" }, { "metadata": { "datalore": { "node_id": "KKT62bidVYF8I147heptAS", "type": "CODE", "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", " //.fold(0.0) { acc: Double, i: Int -> acc + i }\n", "\n", "result" ], "outputs": [], "execution_count": null }, { "metadata": { "datalore": { "node_id": "9qcw7ejFSF8QnAGdf1bAI0", "type": "CODE", "hide_input_from_viewers": true, "hide_output_from_viewers": true } }, "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" ], "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": [ "import java.nio.file.*\n", "\n", "\n", "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": [ "open class Bad {\n", " val value: Int = requestValue()\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 }, { "metadata": {}, "cell_type": "code", "source": [ "class BadString {\n", " val value: String = requestValue()\n", "\n", " fun requestValue(): String {\n", " doSomethingElse()\n", " return \"2\"\n", " }\n", "\n", " private fun doSomethingElse() {\n", " println(value)\n", " }\n", "}\n", "\n", "BadString()" ], "outputs": [], "execution_count": null }, { "cell_type": "code", "source": [ "//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", "private fun requestValue(): Int = TODO()\n", "\n", "// This is the factory-function\n", "fun Good(): Good = Good(requestValue())\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\n", " get() {\n", " println(\"got\")\n", " return 33\n", " }\n", "}\n", "\n", "val lazyClass = ClassWithALazyProperty()\n", "println(lazyClass.lazyValue)\n", "println(lazyClass.lazyValue)\n", "println(lazyClass.getterValue)\n", "println(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": [ "//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", " * 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).forEachOdd {\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", " * 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 }, { "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 }