Kotlin - статически типизированный язык для JVM, Android и браузера. Язык полностью совместим c Java. Более детальная информация здесь.
1// Однострочные комментарии начинаются с //
2/*
3А вот так выглядят многострочные комментарии.
4*/
5
6// Ключевое слово "package" действует и используется // абсолютно также, как и в Java.
7package com.learnxinyminutes.kotlin
8
9/*
10Точкой входа в программу на языке Kotlin является функция "main".
11Приведенная ниже функция передает массив, содержащий любые аргументы из командной строки.
12*/
13fun main(args: Array<String>) {
14 /*
15 Объявление значений производится с помощью или "var", или "val".
16 Значения объявленные с помощью "val" не могут быть изменены или перезаписаны, в то время как объявленные с помощью "var" - могут.
17 */
18 val fooVal = 10 // мы не можем потом изменить значение fooVal на какое-либо иное
19 var fooVar = 10
20 fooVar = 20 // значение fooVar затем может быть изменено.
21
22 /*
23 В большинстве случаев Kotlin самостоятельно может определить тип переменной, поэтому нам не нужно явно указывать его каждый раз.
24 Мы можем явно объявить тип переменной следующим образом:
25 */
26 val foo: Int = 7
27
28 /*
29 Строки могут быть представлены тем же образом, что и в Java.
30 Для экранирования используется обратный слэш.
31 */
32 val fooString = "My String Is Here!"
33 val barString = "Printing on a new line?\nNo Problem!"
34 val bazString = "Do you want to add a tab?\tNo Problem!"
35 println(fooString)
36 println(barString)
37 println(bazString)
38
39 /*
40 Необработанная строка разделяется тройной кавычкой (""").
41 Необработанные строки могут содержать символы новой строки и любые другие символы.
42 */
43 val fooRawString = """
44fun helloWorld(val name : String) {
45 println("Hello, world!")
46}
47"""
48 println(fooRawString)
49
50 /*
51 Строки могут содержать в себе шаблонные выражения.
52 Шаблонные выражения начинаются со знака доллара ($).
53 */
54 val fooTemplateString = "$fooString has ${fooString.length} characters"
55 println(fooTemplateString)
56
57 /*
58 Переменная, которая содержит null должна быть явно обозначена как nullable.
59 Переменная может быть обозначена как nullable с помощью добавления знака вопроса(?) к ее типу.
60 Мы можем получить доступ к nullable переменной используя оператор ?. .
61 Для того, чтобы указать иное значение, если переменная является null, мы используем оператор ?: .
62 */
63 var fooNullable: String? = "abc"
64 println(fooNullable?.length) // => 3
65 println(fooNullable?.length ?: -1) // => 3
66 fooNullable = null
67 println(fooNullable?.length) // => null
68 println(fooNullable?.length ?: -1) // => -1
69
70 /*
71 Функции могут быть объявлены с помощью ключевого слова "fun".
72 Аргументы функции указываются в скобках после имени функции.
73 Аргументы функции также могу иметь и значение по умолчанию.
74 Если требуется, то тип возвращаемого функцией значения, может быть указан после аргументов.
75 */
76 fun hello(name: String = "world"): String {
77 return "Hello, $name!"
78 }
79 println(hello("foo")) // => Hello, foo!
80 println(hello(name = "bar")) // => Hello, bar!
81 println(hello()) // => Hello, world!
82
83 /*
84 Параметр функции может быть отмечен с помощью ключевого слова "vararg", для того чтобы позволить аргументам попасть в функцию.
85 */
86 fun varargExample(vararg names: Int) {
87 println("Argument has ${names.size} elements")
88 }
89 varargExample() // => Argument has 0 elements
90 varargExample(1) // => Argument has 1 elements
91 varargExample(1, 2, 3) // => Argument has 3 elements
92
93 /*
94 Если функция состоит из одиночного выражения, фигурные скобки могут быть опущены. Тело функции указывается после знака = .
95 */
96 fun odd(x: Int): Boolean = x % 2 == 1
97 println(odd(6)) // => false
98 println(odd(7)) // => true
99
100 // Если возвращаемый тип может быть выведен, то нам не нужно его дополнительно указывать.
101 fun even(x: Int) = x % 2 == 0
102 println(even(6)) // => true
103 println(even(7)) // => false
104
105 // Функции могут брать другие функции в качестве аргументов, а также могут возвращать функции.
106 fun not(f: (Int) -> Boolean): (Int) -> Boolean {
107 return {n -> !f.invoke(n)}
108 }
109 // Именованные функции могут быть определены в качестве аргументов с помощью оператора :: .
110 val notOdd = not(::odd)
111 val notEven = not(::even)
112 // Lambda-выражения могут быть определены в качестве аргументов.
113 val notZero = not {n -> n == 0}
114 /*
115 Если lambda-выражение имеет только один параметр, то ее определение может быть опущено (вместе с ->).
116 Имя этого единственного параметра будет "it".
117 */
118 val notPositive = not {it > 0}
119 for (i in 0..4) {
120 println("${notOdd(i)} ${notEven(i)} ${notZero(i)} ${notPositive(i)}")
121 }
122
123 // Ключевое слово "class" используется для
124 // объявления классов.
125 class ExampleClass(val x: Int) {
126 fun memberFunction(y: Int): Int {
127 return x + y
128 }
129
130 infix fun infixMemberFunction(y: Int): Int {
131 return x * y
132 }
133 }
134 /*
135 Чтобы создать новый экземпляр класса, нужно вызвать конструктор.
136 Обратите внимание, что в Kotlin нет ключевого слова "new".
137 */
138 val fooExampleClass = ExampleClass(7)
139 // Функции-члены могут быть вызваны с использованием точечной нотации.
140 println(fooExampleClass.memberFunction(4)) // => 11
141 /*
142 В случае, если функция была помечена ключевым словом "infix", она может быть вызвана с помощью инфиксной нотации.
143 */
144 println(fooExampleClass infixMemberFunction 4) // => 28
145
146 /*
147 Data-классы - это компактный способ создать классы, которые лишь хранят данные.
148 Методы "hashCode"/"equals" и "toString" генерируютсяч автоматически.
149 */
150 data class DataClassExample (val x: Int, val y: Int, val z: Int)
151 val fooData = DataClassExample(1, 2, 4)
152 println(fooData) // => DataClassExample(x=1, y=2, z=4)
153
154 // Data-классы обладают функцией "copy".
155 val fooCopy = fooData.copy(y = 100)
156 println(fooCopy) // => DataClassExample(x=1, y=100, z=4)
157
158 // Объекты могут быть деструктурированы на множество переменных.
159 val (a, b, c) = fooCopy
160 println("$a $b $c") // => 1 100 4
161
162 // Деструктурирование в цикле "for"
163 for ((a, b, c) in listOf(fooData)) {
164 println("$a $b $c") // => 1 100 4
165 }
166
167 val mapData = mapOf("a" to 1, "b" to 2)
168 // Map.Entry также может быть дествуктурирован
169 for ((key, value) in mapData) {
170 println("$key -> $value")
171 }
172
173 // Функция "with" аналогична оператору "with" в JavaScript.
174 data class MutableDataClassExample (var x: Int, var y: Int, var z: Int)
175 val fooMutableData = MutableDataClassExample(7, 4, 9)
176 with (fooMutableData) {
177 x -= 2
178 y += 2
179 z--
180 }
181 println(fooMutableData) // => MutableDataClassExample(x=5, y=6, z=8)
182
183 /*
184 Можно создать список с помощью функции "ListOf".
185 Этот список будет неизменяемым, т.е. элементы не могут быть удалены или добавлены в него.
186 */
187 val fooList = listOf("a", "b", "c")
188 println(fooList.size) // => 3
189 println(fooList.first()) // => a
190 println(fooList.last()) // => c
191 // Элементы списка доступны по их индексу в нем.
192 println(fooList[1]) // => b
193
194 // Изменяемый список может быть создан спомощью функции "mutableListOf".
195 val fooMutableList = mutableListOf("a", "b", "c")
196 fooMutableList.add("d")
197 println(fooMutableList.last()) // => d
198 println(fooMutableList.size) // => 4
199
200 // Мы можем создать набор, используя функцию "setOf".
201 val fooSet = setOf("a", "b", "c")
202 println(fooSet.contains("a")) // => true
203 println(fooSet.contains("z")) // => false
204
205 // Мы можем создать отображение (map), используя функцию "mapOf".
206 val fooMap = mapOf("a" to 8, "b" to 7, "c" to 9)
207 // Получить доступ к значениям отображения (map) можно с помощью их ключа.
208 println(fooMap["a"]) // => 8
209
210 /*
211 Последовательности представляют собой коллекции с ленивой оценкой.
212 Мы можем создать последовательность, используя функцию "generateSequence".
213 */
214 val fooSequence = generateSequence(1, { it + 1 })
215 val x = fooSequence.take(10).toList()
216 println(x) // => [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
217
218 // Пример использования последовательности для генерации чисел Фибоначчи:
219 fun fibonacciSequence(): Sequence<Long> {
220 var a = 0L
221 var b = 1L
222
223 fun next(): Long {
224 val result = a + b
225 a = b
226 b = result
227 return a
228 }
229
230 return generateSequence(::next)
231 }
232 val y = fibonacciSequence().take(10).toList()
233 println(y) // => [1, 1, 2, 3, 5, 8, 13, 21, 34, 55]
234
235 // Kotlin предоставляет функции высшего порядка для работы с коллекциями.
236 val z = (1..9).map {it * 3}
237 .filter {it < 20}
238 .groupBy {it % 2 == 0}
239 .mapKeys {if (it.key) "even" else "odd"}
240 println(z) // => {odd=[3, 9, 15], even=[6, 12, 18]}
241
242 // Цикл "for" может использоваться со всем, что предоставляет итератор.
243 for (c in "hello") {
244 println(c)
245 }
246
247 // Циклы "while" работают также, как и в других языках.
248 var ctr = 0
249 while (ctr < 5) {
250 println(ctr)
251 ctr++
252 }
253 do {
254 println(ctr)
255 ctr++
256 } while (ctr < 10)
257
258 /*
259 "if" может быть использован в качестве выражения, которое возвращает значение.
260 По этой причине в Kotlin тернарный оператор ?: не нужен.
261 */
262 val num = 5
263 val message = if (num % 2 == 0) "even" else "odd"
264 println("$num is $message") // => 5 is odd
265
266 // "when" может быть использован как альтернатива цепочке "if-else if".
267 val i = 10
268 when {
269 i < 7 -> println("first block")
270 fooString.startsWith("hello") -> println("second block")
271 else -> println("else block")
272 }
273
274 // "when" может быть использован с аргументами.
275 when (i) {
276 0, 21 -> println("0 or 21")
277 in 1..20 -> println("in the range 1 to 20")
278 else -> println("none of the above")
279 }
280
281 // "when" также может быть использовано как функция, возвращающая значение.
282 var result = when (i) {
283 0, 21 -> "0 or 21"
284 in 1..20 -> "in the range 1 to 20"
285 else -> "none of the above"
286 }
287 println(result)
288
289 /*
290 Мы можем проверить, что объект принадлежит к определенному типу, используя оператор "is".
291 Если объект проходит проверку типа, то он может использоваться как этот тип без явной его передачи.
292 */
293 fun smartCastExample(x: Any) : Boolean {
294 if (x is Boolean) {
295 // x is automatically cast to Boolean
296 return x
297 } else if (x is Int) {
298 // x is automatically cast to Int
299 return x > 0
300 } else if (x is String) {
301 // x is automatically cast to String
302 return x.isNotEmpty()
303 } else {
304 return false
305 }
306 }
307 println(smartCastExample("Hello, world!")) // => true
308 println(smartCastExample("")) // => false
309 println(smartCastExample(5)) // => true
310 println(smartCastExample(0)) // => false
311 println(smartCastExample(true)) // => true
312
313 // Smartcast также работает с блоком "when"
314 fun smartCastWhenExample(x: Any) = when (x) {
315 is Boolean -> x
316 is Int -> x > 0
317 is String -> x.isNotEmpty()
318 else -> false
319 }
320
321 /*
322 Расширения - это способ добавить новую функциональность к классу.
323 Это то же самое, что методы расширений в C#.
324 */
325 fun String.remove(c: Char): String {
326 return this.filter {it != c}
327 }
328 println("Hello, world!".remove('l')) // => Heo, word!
329
330 println(EnumExample.A) // => A
331 println(ObjectExample.hello()) // => hello
332}
333
334// Enum-классы схожи с типами enum в Java.
335enum class EnumExample {
336 A, B, C
337}
338
339/*
340Ключевое слово "object" может использоваться для создания одноэлементных объектов.
341Мы не можем его инстанцировать, но можем вызывать его уникальный экземпляр по имени.
342Это похоже на одиночные объекты Scala.
343*/
344object ObjectExample {
345 fun hello(): String {
346 return "hello"
347 }
348}
349
350fun useObject() {
351 ObjectExample.hello()
352 val someRef: Any = ObjectExample // we use objects name just as is
353}