diff --git a/uk-ua/kotlin-ua.html.markdown b/uk-ua/kotlin-ua.html.markdown new file mode 100644 index 00000000..f32a43cd --- /dev/null +++ b/uk-ua/kotlin-ua.html.markdown @@ -0,0 +1,463 @@ +--- +language: kotlin +contributors: + - ["S Webber", "https://github.com/s-webber"] +translators: + = ["AstiaSun", "https://github.com/AstiaSun"] +filename: LearnKotlin.kt +lang: uk-ua +--- + +Kotlin - це мова програмування зі статичною типізацією для JVM, Android та браузера. Вона має 100% сумісність із Java. + +[Детальніше](https://kotlinlang.org/) + +```kotlin +// Однорядкові коментарі починаються з // +/* +Такий вигляд мають багаторядкові коментарі +*/ + +// Ключове слово package працює так само, як і в Java. +package com.learnxinyminutes.kotlin + +/* +Точкою входу для програм на Kotlin є функція під назвою main. +Вона приймає масив із аргументів, що були передані через командний рядок. +Починаючи з Kotlin 1.3, функція main може бути оголошена без параметрів взагалі. +*/ +fun main(args: Array) { + /* + Оголошення змінних відбувається за допомогою ключових слів var або val. + Відмінність між ними полягає в тому, що значення змінних, оголошених через + val, не можна змінювати. Водночас, змінній "var" можна переприсвоїти нове + значення в подальшому. + */ + val fooVal = 10 // більше ми не можемо змінити значення fooVal на інше + var fooVar = 10 + fooVar = 20 // fooVar може змінювати значення + + /* + В більшості випадків Kotlin може визначати, якого типу змінна, тому не + потрібно щоразу точно вказувати її тип. + Тип змінної вказується наступним чином: + */ + val foo: Int = 7 + + /* + Рядки мають аналогічне з Java представлення. Спеціальні символи + позначаються за допомогою зворотнього слеша. + */ + val fooString = "My String Is Here!" + val barString = "Printing on a new line?\nNo Problem!" + val bazString = "Do you want to add a tab?\tNo Problem!" + println(fooString) + println(barString) + println(bazString) + + /* + Необроблений рядок розмежовується за допомогою потрійтих лапок ("""). + Необроблені рядки можуть містити переніс рядка (не спеціальний символ \n) та + будь-які інші символи. + */ + val fooRawString = """ +fun helloWorld(val name : String) { + println("Hello, world!") +} +""" + println(fooRawString) + + /* + Рядки можуть містити шаблонні вирази. + Шаблонний вираз починається із символа доллара "$". + */ + val fooTemplateString = "$fooString has ${fooString.length} characters" + println(fooTemplateString) // => My String Is Here! has 18 characters + + /* + Щоб змінна могла мати значення null, потрібно це додатково вказати. + Для цього після оголошеного типу змінної додається спеціальний символ "?". + Отримати значення такої змінної можна використавши оператор "?.". + Оператор "?:" застосовується, щоб оголосити альтернативне значення змінної + у випадку, якщо вона буде рівна null. + */ + var fooNullable: String? = "abc" + println(fooNullable?.length) // => 3 + println(fooNullable?.length ?: -1) // => 3 + fooNullable = null + println(fooNullable?.length) // => null + println(fooNullable?.length ?: -1) // => -1 + + /* + Функції оголошуються з використанням ключового слова fun. + Аргументи функції перелічуються у круглих дужках після назви функції. + Аргументи можуть мати значення за замовчуванням. Тип значення, що повертатиметься + функцією, вказується після оголошення аргументів за необхідністю. + */ + fun hello(name: String = "world"): String { + return "Hello, $name!" + } + println(hello("foo")) // => Hello, foo! + println(hello(name = "bar")) // => Hello, bar! + println(hello()) // => Hello, world! + + /* + Параменти функції можуть бути помічені ключовим словом vararg. Це дозволяє + приймати довільну кількість аргументів функції зазначеного типу. + */ + fun varargExample(vararg names: Int) { + println("Argument has ${names.size} elements") + } + varargExample() // => Argument has 0 elements + varargExample(1) // => Argument has 1 elements + varargExample(1, 2, 3) // => Argument has 3 elements + + /* + Коли фукнція складається з одного виразу, фігурні дужки не є обов'язковими. + Тіло функції вказується після оператора "=". + */ + fun odd(x: Int): Boolean = x % 2 == 1 + println(odd(6)) // => false + println(odd(7)) // => true + + // Якщо значення, що повертається функцією, може бути однозначно визначено, + // його непотрібно вказувати. + fun even(x: Int) = x % 2 == 0 + println(even(6)) // => true + println(even(7)) // => false + + // Функції можуть приймати інші функції як аргументи, а також повертати інші функції. + fun not(f: (Int) -> Boolean): (Int) -> Boolean { + return {n -> !f.invoke(n)} + } + // Іменовані функції можуть бути вказані як аргументи за допомогою оператора "::". + val notOdd = not(::odd) + val notEven = not(::even) + // Лямбда-вирази також можуть бути аргументами функції. + val notZero = not {n -> n == 0} + /* + Якщо лямбда-вираз приймає лише один параметр, його оголошення може бути пропущене + (разом із ->). Всередині виразу до цього параметра можна звернутись через + змінну "it". + */ + val notPositive = not {it > 0} + for (i in 0..4) { + println("${notOdd(i)} ${notEven(i)} ${notZero(i)} ${notPositive(i)}") + } + + // Ключове слово class використовується для оголошення класів. + class ExampleClass(val x: Int) { + fun memberFunction(y: Int): Int { + return x + y + } + + infix fun infixMemberFunction(y: Int): Int { + return x * y + } + } + /* + Щоб створити новий об'єкт, потрібно викликали конструктор класу. + Зазначте, що в Kotlin немає ключового слова new. + */ + val fooExampleClass = ExampleClass(7) + // Методи класу викликаються через крапку. + println(fooExampleClass.memberFunction(4)) // => 11 + /* + Якщо функція була позначена ключовим словом infix, тоді її можна викликати через + інфіксну нотацію. + */ + println(fooExampleClass infixMemberFunction 4) // => 28 + + /* + Класи даних - це лаконічний спосіб створювати класи, що містимуть тільки дані. + Методи "hashCode"/"equals" та "toString" автоматично генеруються. + */ + data class DataClassExample (val x: Int, val y: Int, val z: Int) + val fooData = DataClassExample(1, 2, 4) + println(fooData) // => DataClassExample(x=1, y=2, z=4) + + // Класи даних також мають функцію "copy". + val fooCopy = fooData.copy(y = 100) + println(fooCopy) // => DataClassExample(x=1, y=100, z=4) + + // Об'єкти можуть бути знищенні кількома способами. + val (a, b, c) = fooCopy + println("$a $b $c") // => 1 100 4 + + // знищення у циклі for + for ((a, b, c) in listOf(fooData)) { + println("$a $b $c") // => 1 100 4 + } + + val mapData = mapOf("a" to 1, "b" to 2) + // Map.Entry також знищується + for ((key, value) in mapData) { + println("$key -> $value") + } + + // Функція із "with" працює майже так само як це ж твердження у JavaScript. + data class MutableDataClassExample (var x: Int, var y: Int, var z: Int) + val fooMutableData = MutableDataClassExample(7, 4, 9) + with (fooMutableData) { + x -= 2 + y += 2 + z-- + } + println(fooMutableData) // => MutableDataClassExample(x=5, y=6, z=8) + + /* + Стисок можна створити використовуючи функцію listOf. + Список буде незмінним, тобто елементи не можна буде додавати або видаляти. + */ + val fooList = listOf("a", "b", "c") + println(fooList.size) // => 3 + println(fooList.first()) // => a + println(fooList.last()) // => c + // доступ до елементів злійснюється через їх порядковий номер. + println(fooList[1]) // => b + + // Змінні списки можна створити використовуючи функцію mutableListOf. + val fooMutableList = mutableListOf("a", "b", "c") + fooMutableList.add("d") + println(fooMutableList.last()) // => d + println(fooMutableList.size) // => 4 + + // Функція setOf створює об'єкт типу множина. + val fooSet = setOf("a", "b", "c") + println(fooSet.contains("a")) // => true + println(fooSet.contains("z")) // => false + + // mapOf створює асоціативний масив. + val fooMap = mapOf("a" to 8, "b" to 7, "c" to 9) + // Доступ до значень в асоціативних масивах здійснюється через їх ключі. + println(fooMap["a"]) // => 8 + + /* + Послідовності представленні як колекції лінивих обчислень. Функція generateSequence + створює послідовність. + */ + val fooSequence = generateSequence(1, { it + 1 }) + val x = fooSequence.take(10).toList() + println(x) // => [1, 2, 3, 4, 5, 6, 7, 8, 9, 10] + + // Приклад використання послідовностей, генерація чисел Фібоначі: + fun fibonacciSequence(): Sequence { + var a = 0L + var b = 1L + + fun next(): Long { + val result = a + b + a = b + b = result + return a + } + + return generateSequence(::next) + } + val y = fibonacciSequence().take(10).toList() + println(y) // => [1, 1, 2, 3, 5, 8, 13, 21, 34, 55] + + // Kotlin має функції вищого порядку для роботи з колекціями. + val z = (1..9).map {it * 3} + .filter {it < 20} + .groupBy {it % 2 == 0} + .mapKeys {if (it.key) "even" else "odd"} + println(z) // => {odd=[3, 9, 15], even=[6, 12, 18]} + + // Цикл for може використовуватись з будь-чим, що має ітератор. + for (c in "hello") { + println(c) + } + + // Принцип роботи циклів "while" не відрізняється від інших мов програмування. + var ctr = 0 + while (ctr < 5) { + println(ctr) + ctr++ + } + do { + println(ctr) + ctr++ + } while (ctr < 10) + + /* + if може бути використаний як вираз, що повертає значення. Тому тернарний + оператор ?: не потрібний в Kotlin. + */ + val num = 5 + val message = if (num % 2 == 0) "even" else "odd" + println("$num is $message") // => 5 is odd + + // "when" використовується як альтернатива в ланцюзі "if-else if". + val i = 10 + when { + i < 7 -> println("first block") + fooString.startsWith("hello") -> println("second block") + else -> println("else block") + } + + // "when" може приймати аргумент. + when (i) { + 0, 21 -> println("0 or 21") + in 1..20 -> println("in the range 1 to 20") + else -> println("none of the above") + } + + // "when" також може використовуватись як функція, що повератє значення. + var result = when (i) { + 0, 21 -> "0 or 21" + in 1..20 -> "in the range 1 to 20" + else -> "none of the above" + } + println(result) + + /* + Тип об'єкта можна перевірити використавши оператор is. Якщо перевірка проходить + успішно, тоді можна використовувати об'єкт як данний тип не приводячи до ного + додатково. + */ + fun smartCastExample(x: Any) : Boolean { + if (x is Boolean) { + // x тепер має тип Boolean + return x + } else if (x is Int) { + // x тепер має тип Int + return x > 0 + } else if (x is String) { + // x тепер має тип String + return x.isNotEmpty() + } else { + return false + } + } + println(smartCastExample("Hello, world!")) // => true + println(smartCastExample("")) // => false + println(smartCastExample(5)) // => true + println(smartCastExample(0)) // => false + println(smartCastExample(true)) // => true + + // Smartcast (розумне приведення) також працює з блоком when + fun smartCastWhenExample(x: Any) = when (x) { + is Boolean -> x + is Int -> x > 0 + is String -> x.isNotEmpty() + else -> false + } + + /* + Розширення - це ще один спобів розширити функціонал класу. + Подібні методи розширення реалізовані у С#. + */ + fun String.remove(c: Char): String { + return this.filter {it != c} + } + println("Hello, world!".remove('l')) // => Heo, word! +} + +// Класи перелічення також подібні до тих типів, що і в Java. +enum class EnumExample { + A, B, C // Константи перелічення розділені комами. +} +fun printEnum() = println(EnumExample.A) // => A + +// Оскільки кожне перелічення - це об'єкт класу enum, воно може бути +// проініціалізоване наступним чином: +enum class EnumExample(val value: Int) { + A(value = 1), + B(value = 2), + C(value = 3) +} +fun printProperty() = println(EnumExample.A.value) // => 1 + +// Кожне перелічення має властивості, які дозволяють отримати його ім'я +// та порядок (позицію) в класі enum: +fun printName() = println(EnumExample.A.name) // => A +fun printPosition() = println(EnumExample.A.ordinal) // => 0 + +/* +Ключове слово object можна виокристати для створення об'єкту сінглтону. Об'єкт не +можна інстанціювати, проте на його унікальний екземпляр можна посилатись за іменем. +Подібна можливість є в сінглтон об'єктах у Scala. +*/ +object ObjectExample { + fun hello(): String { + return "hello" + } + + override fun toString(): String { + return "Hello, it's me, ${ObjectExample::class.simpleName}" + } +} + + +fun useSingletonObject() { + println(ObjectExample.hello()) // => hello + // В Kotlin, "Any" - це корінь ієрархії класів, так само, як і "Object" у Java. + val someRef: Any = ObjectExample + println(someRef) // => Hello, it's me, ObjectExample +} + + +/* +Оператор перевірки на те, що об'єкт не рівний null, (!!) перетворює будь-яке значення в ненульовий тип і кидає ексепшн, якщо значення рівне null. +*/ +var b: String? = "abc" +val l = b!!.length + +// Далі - приклади перевизначення методів класу Any в класі-насліднику +data class Counter(var value: Int) { + // перевизначити Counter += Int + operator fun plusAssign(increment: Int) { + this.value += increment + } + + // перевизначити Counter++ та ++Counter + operator fun inc() = Counter(value + 1) + + // перевизначити Counter + Counter + operator fun plus(other: Counter) = Counter(this.value + other.value) + + // перевизначити Counter * Counter + operator fun times(other: Counter) = Counter(this.value * other.value) + + // перевизначити Counter * Int + operator fun times(value: Int) = Counter(this.value * value) + + // перевизначити Counter in Counter + operator fun contains(other: Counter) = other.value == this.value + + // перевизначити Counter[Int] = Int + operator fun set(index: Int, value: Int) { + this.value = index + value + } + + // перевизначити виклик екземпляру Counter + operator fun invoke() = println("The value of the counter is $value") + +} +// Можна також перевизначити оператори через методи розширення. +// перевизначити -Counter +operator fun Counter.unaryMinus() = Counter(-this.value) + +fun operatorOverloadingDemo() { + var counter1 = Counter(0) + var counter2 = Counter(5) + counter1 += 7 + println(counter1) // => Counter(value=7) + println(counter1 + counter2) // => Counter(value=12) + println(counter1 * counter2) // => Counter(value=35) + println(counter2 * 2) // => Counter(value=10) + println(counter1 in Counter(5)) // => false + println(counter1 in Counter(7)) // => true + counter1[26] = 10 + println(counter1) // => Counter(value=36) + counter1() // => The value of the counter is 36 + println(-counter2) // => Counter(value=-5) +} +``` + +### Подальше вивчення + +* [Уроки Kotlin](https://kotlinlang.org/docs/tutorials/) +* [Спробувати попрацювати з Kotlin в браузері](https://play.kotlinlang.org/) +* [Список корисних посилань](http://kotlin.link/)