Академический Документы
Профессиональный Документы
Культура Документы
Kotlin
Kotlin
ЯЗЫК
ПРОГРАММИРОВАНИЯ
KOTLIN
Киев
2017
УДК 004.4*Kotlin
П32
Пименов, Сергей
П32 Язык программирования Kotlin / Сергей Пименов — К. : «Агентство
«IPIO», 2017. — 304 с.
ISBN 978-617-7453-28-3
Книга представляет собой полное справочное пособие по язы-
ку программирования Kotlin. В книге подробно рассмотрены такие
вопросы как: типы данных, базовые синтаксические конструкции
языка, вопросы объектно-ориентированного программирования,
классы и интерфейсы, исключения. Книга изобилует примерами
кода, который можно загрузить из репозитория автора. Книга рас-
считана на разработчиков разной квалификации и будет полезна
как новичкам в программировании, так и опытным программи-
стам, решившим освоить новый отличный язык программирования
Kotlin.
УДК 004.4*Kotlin
ПРЕДИСЛОВИЕ
Структура книги
3
Язык программирования Kotlin
АВТОР ВЫРАЖАЕТ
БЛАГОДАРНОСТЬ
Любимой жене Татьяне за любовь и поддержку.
Mirohost
4
СОДЕРЖАНИЕ
6
Содержание
7
Язык программирования Kotlin
ГЛАВА 1.
ИСТОРИЯ И РАЗВИТИЕ
ЯЗЫКА
JETBRAINS
Компания Jetbrains была основана в 2000 году тремя программистами:
Сергеем Дмитриевым, Евгением Беляевым и Валентином Кипятковым
с основной целью — создать мощную, полноценную IDE (интегриро-
ванная среда разработки) для Java. Штаб-квартира компании находит-
ся в Чехии, но JetBrains имеет множество представительств и в других
странах. В штате компании более 500 разработчиков, которые рабо-
тают в Санкт-Петербурге, Мюнхене, Праге, Бостоне и Москве и соз-
дают интеллектуальные инструменты, понимающие семантику кода
и повышающие продуктивность работы программистов. На данный
момент JetBrains сотрудничает с более чем 3000 компаний по всему
миру и в различных сферах деятельности: банки, финансирование, IT-
индустрия, биотехнологии, промышленность, программные продукты
и многое другое.
Первым продуктом компании был Renamer — небольшая програм-
ма, которая позволяла делать простой рефакторинг-переименование
для программ на языке Java. Программа давала возможность безопасно
переименовывать класс, пакет, метод или переменную в проекте. Вто-
рым продуктом стал CodeSearch — плагин для популярной в то время
IDE от Borland JBuilder, который позволял быстро и точно находить все
использования символа, метода или класса во всей программе.
8
Глава 1. История и развитие языка
KOTLIN
В 2010 году компания Jetbrains приступила к созданию нового языка
программирования. Необходимость такого решения была обусловле-
на несколькими факторами, в том числе и тем, что стало понятно, что
язык Java в некоторых вопросах уже не устраивает компанию, а суще-
ствующие альтернативные языки не соответствуют тем требованиям,
которые выдвигались к языку.
В компании собралось очень много людей с большим экспертным
опытом в области языков программирования. Это также способствова-
ло принятию решения о необходимости создать свой новый язык про-
граммирования, который бы удовлетворял требованиям текущего мо-
мента и мог бы решать задачи будущего.
9
Язык программирования Kotlin
10
Глава 1. История и развитие языка
11
Язык программирования Kotlin
ПРИМЕНЕНИЕ KOTLIN
12
Глава 1. История и развитие языка
13
Язык программирования Kotlin
14
Глава 2. Краткий обзор Kotlin
ГЛАВА 2.
КРАТКИЙ ОБЗОР KOTLIN
ООП
Как и в остальных языках программирования, элементы Kotlin суще-
ствуют не сами по себе. Они, скорее, действуют совместно, образуя язык
в целом. Иногда такая взаимосвязанность может затруднять описание
какого-то одного аспекта, не затрагивая ряда других. Зачастую для по-
нимания одного языкового средства необходимо знать другое. В этой
главе представлен краткий обзор языка Kotlin. Приведенный матери-
ал послужит отправной точкой в понимании этого языка. Большинство
рассмотренных здесь вопросов будут подробнее обсуждаться в осталь-
ных главах этой книги.
Объектно-ориентированное программирование
15
Язык программирования Kotlin
Абстракция
16
Глава 2. Краткий обзор Kotlin
Инкапсуляция
Наследование
17
Язык программирования Kotlin
Полиморфизм
18
Глава 2. Краткий обзор Kotlin
Итог
/**
* Это простая программа на Kotlin
*/
// Любая программа на Kotlin стартует
с функции main
fun main(args: Array<String>) {
println(“Привет, мир!”)
}
/**
* Это простая программа на Kotlin
*/
19
Язык программирования Kotlin
20
Глава 2. Краткий обзор Kotlin
println(«Привет, мир!»)
УСТАНОВКА КОМПИЛЯТОРА
Ручная установка
21
Язык программирования Kotlin
$ brew update
$ brew install kotlin
КОМПИЛЯЦИЯ ПРОГРАММЫ
22
Глава 2. Краткий обзор Kotlin
kotlinc -help
Компиляция библиотеки
kotlinc
23
Язык программирования Kotlin
import java.io.File
val folders = File(args[0]).listFiles { file
-> file.isDirectory() }
folders?.forEach { folder -> println(folder)
}
ЛЕКСИКА
Исходный текст программы на языке Kotlin состоит из совокупности
пробелов, идентификаторов, литералов, комментариев, операторов,
разделителей и ключевых слов. Ниже кратко описывается основной
синтаксис языка Kotlin.
Пробелы. Kotlin — язык свободной формы. Это означает, что при
написании программы не нужно следовать каким-либо специальным
правилам в отношении отступов. Программу «Привет, мир!» можно
написать и в одну строку, и любым другим способом. Единственное
24
Глава 2. Краткий обзор Kotlin
100
98.7
‘X’
“Это строковый литерал”
25
Язык программирования Kotlin
БАЗОВЫЙ СИНТАКСИС
Цель данного раздела — ознакомить вас с базовым синтаксисом языка
Kotlin. В дальнейшем все аспекты языка будут рассмотрены подробнее.
package my.demo
import java.util.*
// ...
Определение функции
26
Глава 2. Краткий обзор Kotlin
val a: Int = 1
val b = 1 // Тип `Int` выведен
автоматически
val c: Int // Тип обязателен, когда
значение не инициализируется
c = 1 // последующее присвоение
Изменяемая переменная:
27
Язык программирования Kotlin
Комментарии
// однострочный комментарий
/* Блочный комментарий
из нескольких строк. */
Строковые шаблоны
var a = 1
// простое имя переменной в шаблоне:
val s1 = «a is $a»
a = 2
// Произвольное выражение в шаблоне:
val s2 = “${s1.replace(“is”, “was”)}, but
now is $a”
Условные выражения
28
Глава 2. Краткий обзор Kotlin
Или
// ...
if (x == null) {
29
Язык программирования Kotlin
Или
30
Глава 2. Краткий обзор Kotlin
Или даже
Цикл for
Или
31
Язык программирования Kotlin
Выражение when
Интервалы
32
Глава 2. Краткий обзор Kotlin
if (x in 1..y-1)
print(“OK”)
if (x !in 0..array.lastIndex)
print(“Out”)
Итерация по интервалу:
for (x in 1..5)
print(x)
Коллекции
33
Язык программирования Kotlin
names
.filter { it.startsWith(«A») }
.sortedBy { it }
.map { it.toUpperCase() }
.forEach { print(it) }
Интерфейсы и классы
class Invoice {
//..
}
interface MyInterface {
val prop: Int // abstract
val propertyWithImplementation: String
34
Глава 2. Краткий обзор Kotlin
get() = “foo”
fun foo() {
print(prop)
}
}
class Child : MyInterface {
override val prop: Int = 29
}
КЛЮЧЕВЫЕ СЛОВА
В отличие от других языков программирования язык Kotlin поддержи-
вает концепцию «жестких» и «мягких» ключевых слов. Их различие со-
стоит в том, что «жесткие» ключевые слова не могут быть использова-
ны в качестве идентификаторов, в отличие «мягких».
В настоящее время в языке Kotlin определено 74 ключевых слова, ко-
торые вместе с синтаксисом операторов и разделителей образуют осно-
ву языка Kotlin.
false is in throw
35
Язык программирования Kotlin
header impl
ПАКЕТЫ
Пакеты являются контейнерами для классов и функций. Они служат
для разделения пространства имен. Например, можно создать класс
List, чтобы хранить его в отдельном пакете и больше не беспокоить-
ся о возможных конфликтах с другими классами с таким же названием
и хранящимися в другом месте.
К пакету применимы области видимости. Это значит, что в пакете
можно определить классы и функции, недоступные для кода за предела-
ми этого пакета, или такию которые не будут доступны в объявлениях
36
Глава 2. Краткий обзор Kotlin
package foo.bar
// ...
package пакет1[.пакет2[.пакет3]]
Импорт пакетов
37
Язык программирования Kotlin
import пакет1[.пакет2].(имя_объекта | *)
import foo.Bar
или доступное содержимое пространства имён
(пакет, класс, объект и т.д.)
import foo.* // всё в ‘foo’ становится
доступно без указания пакета
Импорт по умолчанию
38
Глава 2. Краткий обзор Kotlin
kotlin.*
kotlin.annotation.*
kotlin.collections.*
kotlin.comparisons.* (since 1.1)
kotlin.io.*
kotlin.ranges.*
kotlin.sequences.*
kotlin.text.*
JVM:
java.lang.*
kotlin.jvm.*
JS:
kotlin.js.*
import java.util.*
class MyDate: Date() {
}
39
Язык программирования Kotlin
40
Глава 3. Типы данных и переменные
ГЛАВА 3.
ТИПЫ ДАННЫХ
И ПЕРЕМЕННЫЕ
ТИПЫ ДАННЫХ
Kotlin — строго типизированный язык. Это значит, что каждая пере-
менная и каждое выражение имеет конкретный тип, и каждый тип
строго определен. Также все операции присваивания, как явные, так
и через параметры, передаваемые при вызове методов, проверяются
на соответствие типов. Компилятор проверяет все выражения и пара-
метры на соответствие типов. Любые несоответствия типов считаются
ошибками, которые должны быть исправлены до завершения процес-
са компиляции.
Kotlin реализует следующий набор типов:
41
Язык программирования Kotlin
ЧИСЛА
Kotlin обрабатывает численные типы примерно так же, как и Java, хотя
некоторые различия все же имеются. Например, отсутствует неявное
расширяющее преобразование для чисел, а литералы в некоторых слу-
чаях немного отличаются.
Для представления чисел в Kotlin используются следующие встроен-
ные типы:
Размер
Тип Диапазон значений
(кол-во бит)
Целые числа
42
Глава 3. Типы данных и переменные
Тип Byte
val a: Byte
val b: Byte = 10
Тип Short
val a: Short
val b: Short = 255
Тип Int
val a: Int
val b: Int = 10000
Тип Long
val a: Long
val b: Long = 16070400000000
/**
* Пример программы вычисления расстояния,
проходимого светом
*/
43
Язык программирования Kotlin
Тип Float
val a: Float
val a: Float = 3.14
44
Глава 3. Типы данных и переменные
Тип Double
val a: Double
/**
* Пример программы вычисления площади круга
*/
fun main(args: Array<String>) {
val pi: Double = 3.1416
val r: Double = 10.8
val a: Double = pi * r * r
println(“Площадь круга равна $a”)
}
Числовые литералы
45
Язык программирования Kotlin
Представление (обертки)
Явные преобразования
46
Глава 3. Типы данных и переменные
toByte(): Byte
toShort(): Short
toInt(): Int
toLong(): Long
toFloat(): Float
toDouble(): Double
toChar(): Char
Операции
47
Язык программирования Kotlin
(>>> в Java)
and(bits) – побитовое И
or(bits) – побитовое ИЛИ
xor(bits) – побитовое исключающее ИЛИ
inv() – побитовое отрицание
СИМВОЛЫ
Для хранения символов Kotlin использует тип Char. Обратите внима-
ние, что символы в Kotlin напрямую не могут рассматриваться в каче-
стве чисел.
48
Глава 3. Типы данных и переменные
/**
* Программа демонстрирует применение типа
Char
*/
fun main(args: Array<String>) {
val char1: Int = 88
val char2: Char = ‘Y’
println(“Convert int 88 to char
${char1.toChar()}”)
println(“Convert char Y to int ${char2.
toInt()}”)
}
СТРОКИ
Строки в Kotlin представлены типом String. Они являются неизме-
няемыми. Строки состоят из символов, которые могут быть получены
по порядковому номеру: s[i]. Проход по строке выполняется циклом
for:
for (c in str) {
println(c)
}
Строковые литералы
49
Язык программирования Kotlin
Строковые шаблоны
val i = 10
val s = “i = $i” // вычисляется как «i =
10»
50
Глава 3. Типы данных и переменные
val s = “abc”
val str = “$s.length is ${s.length}” //
вычисляется как «abc.length is 3»
Конкатенация строк
/**
* Пример программы работы со строками.
Программа выводит символ и код символа
*/
fun main(args: Array<String>){
val text = “This is a string”
for(c in text) {
print(“$c[${c.toInt()}] “)
}
println()
Println(”The ”+ “end”)
}
МАССИВЫ
Массивы — это группа однотипных переменных, для обращения к ко-
торым используется общее имя. Доступ к конкретному элементу масси-
ва осуществляется по его индексу. Массивы представляют собой удоб-
ный способ группирования связанной вместе информации.
51
Язык программирования Kotlin
52
Глава 3. Типы данных и переменные
for (k in 0..a.size-1) {
print(“${a[k]} “)
}
println()
for (k in b) {
print(“$k “)
}
println()
}
/**
* Пример программы, которая создает 2D
и 3D массивы
*/
fun main(args: Array<String>){
val one: IntArray = intArrayOf(1, 2, 3)
val two: IntArray = intArrayOf(4, 5, 6)
val three: IntArray = intArrayOf(7, 8,
9)
val a2d: Array<IntArray> = arrayOf(one,
two)
val a3d: Array<Array<IntArray>> =
arrayOf(arrayOf(one, two, three))
println(“Print 2d array”)
for (i in a2d) {
for (j in i) {
53
Язык программирования Kotlin
print(j)
}
println()
}
println(“Print 3d array”)
for (i in a3d) {
for (j in i) {
for (k in j) {
print(k)
}
println()
}
println()
}
}
ЛОГИЧЕСКИЙ ТИП
Тип Boolean представляет логический тип данных и принимает два зна-
чения: true и false. При необходимости использования nullable
ссылок логические переменные оборачиваются.
Встроенные действия над логическими переменными включают:
Например:
54
Глава 3. Типы данных и переменные
ПРИВЕДЕНИЕ ТИПОВ
В языке Kotlin, в отличие от Java, нет автоматического преобразования
и продвижения типов. Для того что бы один числовой тип присвоить
другому, необходимо осуществить явное приведение типа.
Операторы is и !is
if (obj is String) {
print(obj.length)
}
if (obj !is String) { // тоже самое что
и !(obj is String)
print(«Not a String»)
}
else {
print(obj.length)
}
Умные приведения
55
Язык программирования Kotlin
when (x) {
is Int -> print(x + 1)
is String -> print(x.length + 1)
is IntArray -> print(x.sum())
}
56
Глава 3. Типы данных и переменные
57
Язык программирования Kotlin
ПСЕВДОНИМЫ ТИПОВ
Начиная с версии 1.1 в Kotlin появился такой полезный функционал,
как псевдонимы типов. Псевдонимы типов предоставляют альтернатив-
ные имена для существующих типов. Если имя типа слишком длинное,
вы можете ввести другое, более короткое имя, и использовать его вме-
сто первоначального.
Псевдонимы типов полезны, когда вы хотите сократить длинные
имена типов, содержащих обобщения. К примеру, можно упрощать на-
звания типов коллекций:
class A {
inner class Inner
}
class B {
inner class Inner
}
typealias AInner = A.Inner
typealias BInner = B.Inner
58
Глава 3. Типы данных и переменные
59
Язык программирования Kotlin
ГЛАВА 4.
ОПЕРАЦИИ
ОПЕРАЦИИ В KOTLIN
В языке Kotlin поддерживается обширный ряд операций. Большинство
из них можно отнести к одной их следующих категорий: арифметиче-
ские, логические, отношения, нахождения и вхождения.
АРИФМЕТИЧЕСКИЕ ОПЕРАЦИИ
Арифметические операции применяются в математических выражени-
ях таким же образом, как и в алгебре. Ниже перечислены арифметиче-
ские операции, доступные в Kotlin:
Операция Описание
* Умножение
60
Глава 4. Операции
Операция Описание
/ Деление
% Деление по модулю
++ Инкремент
+= Сложение с присваиванием
-= Вычитание с присваиванием
*= Умножение с присваиванием
/= Деление с присваиванием
-- Декремент
/**
* Программа демонстрирует основные
арифметические операции
*/
fun main(args: Array<String>) {
61
Язык программирования Kotlin
val a = 1 +1
val b = a * 2
val c = b / 4
val d = c - a
val e = -d
val da: Double = 1.0 + 1.0
val db = da * 3
val dc = db / 4
val dd = dc - a
val de = -dd
println(“Целочисленная арифметика”)
println(«a = $a»)
println(«b = $b»)
println(«c = $c»)
println(«d = $d»)
println(“e = $e”)
println(“\nАрифметика с плавающей
точкой”)
println(«da = $da»)
println(«db = $db»)
println(«dc = $dc»)
println(«dd = $dd»)
println(“de = $de”)
}
62
Глава 4. Операции
/**
* Программа демонстрирует деление по модулю
*/
fun main(args: Array<String>){
val x = 42
val y = 42.25
println(“x mod 10 = ${x % 10}”)
println(“y mod 10 = ${y % 10}”)
}
a = a + 5
b = b - 5
c = c / 5
d = d * 5
e = e % 5
a += 5
b -= 5
c /= 5
d *= 5
e %= 5
63
Язык программирования Kotlin
/**
* Программа демонстрирует работу составных
операций
*/
fun main(args: Array<String>){
var a = 5
var b = 5
var c = 5
var d = 5
var e = 5
a += 5
b -= 5
c /= 5
d *= 5
e %= 5
println(“a += 5 будет $a»)
println(«b -= 5 будет $b»)
println(«c /= 5 будет $c»)
println(«d *= 5 будет $d»)
println(«e %= 5 будет $e»)
}
64
Глава 4. Операции
/**
* Программа демонстрирует применение
операций инкремента и декремента
*/
fun main(args: Array<String>){
var a = 1
var b: Int
println(“Значение а равно $a»)
a++
println(«Выполняем a++ теперь а равно
$a»)
a--
println(«Выполняем a-- теперь а равно
$a»)
b = a++
println(«Выполняем b = a++ теперь b
равно $b а равно $a»)
b = ++a
println(«Выполняем b = ++a теперь b
равно $b а равно $a»)
b = a--
println(«Выполняем b = a-- теперь b
65
Язык программирования Kotlin
ОПЕРАЦИИ ОТНОШЕНИЯ
Операции отношения, или другое название — операции сравнения,
определяют отношение одного операнда к другому. В частности, они
определяют равенство и упорядочивание. Ниже в таблице перечислены
операции отношения, доступные в Kotlin.
Операция Описание
== Равно
!= Не равно
> Больше
< Меньше
66
Глава 4. Операции
Операция Описание
val a: Int = 1
val e: Long = 1
if (a == e) { // Error: Operator ‘==’ can-
not be applied to ‘Int’ and ‘Long’
//..
}
val a: Int = 1
val e: Long = 1
// Теперь оба операнда имеют одинаковый тип
// и их можно сравнивать.
if (a == е.toInt()) {
//..
}
int done;
//...
67
Язык программирования Kotlin
if (done) ...
if (!done) ...
if (done == 0) ...
if (done != 0) ...
/**
* Программа демонстрирует использование
операций отношения
*/
fun main(args: Array<String>){
val a = 1
val b = 3
val c = 1L
val char1 = ‘1’
val char2 = ‘1’
val char3 = ‘2’
val char_equals_result: String
val number1 = Integer(10)
val number2 = Integer(10)
val number3 = number1
println(“Выражение a == b вернет ${a ==
b}»)
println(«Выражение a == c.toInt()
вернет ${a == c.toInt()}»)
println(«Выражение number1 == number2
вернет « + (number1 == number2))
println(«Выражение number1 === number2
вернет « + (number1 === number2))
println(«Выражение number1 === number3
вернет « + (number1 === number3))
println(«Выражение char1 == char2
вернет « + (char1 == char2))
if (char1 < char3) {
println(“char1 меньше char3»)
68
Глава 4. Операции
} else {
println(“char1 больше char3»)
}
char_equals_result = if (char1 ==
char3) “символы равны» else “символы
не равны»
println(“Результат сравнения
на равенство char1 и char3: $char_equals_re-
sult”)
}
РАВЕНСТВО
В языке Kotlin есть два типа равенства:
Равенство ссылок
69
Язык программирования Kotlin
Равенство структур
70
Глава 4. Операции
ЛОГИЧЕСКИЕ ОПЕРАЦИИ
Описываемые в этом разделе логические операции могут использовать-
ся только с операндами типа boolean. Ниже в таблице перечислены
все доступные в языке Kotlin логические операции.
Операция Описание
! Логическая операция НЕ
71
Язык программирования Kotlin
/**
* Программа демонстрирует использование
логических операций
*/
fun main(args: Array<String>){
val a = 1
val b = 2
val c = 0
val d = false
val d = true
val e = true
val f = false
println(“Логические операции”)
println(“Выражение a > 0 && b > 0
вернет ${a > 0 && b > 0}”)
println(“Выражение a > 0 && c > 0
вернет ${a > 0 && c > 0}”)
println(“Выражение a > 0 || c == 0
вернет ${a > 0 && c == 0}”)
println(“Выражение !d вернет ${!d}”)
println(“Поразрядные логические
операции”)
println(“Выражение e and f вернет ${e
and f}”)
println(«Выражение d or e вернет ${d or
f}»)
println(«Выражение d xor e вернет ${d
xor e}»)
}
72
Глава 4. Операции
ПОРАЗРЯДНЫЕ ОПЕРАЦИИ
В языке Kotlin определены несколько поразрядных операций. Эти опе-
рации воздействуют на отдельные двоичные разряды операндов. По-
разрядные операции выполнимы для типов Boolean, Long и Int.
В языке Kotlin, в отличие от других языков, для поразрядных бито-
вых операций вместо особых обозначений используются именованные
функции, которые могут быть вызваны в инфиксной форме. Например:
and(bits) побитовое И
y = x.inv()
73
Язык программирования Kotlin
z = x and y
z = x or y
z = x xor y
z = x shl 2
z = x shr 2
74
Глава 4. Операции
z = x ushr 2
/**
* Программа демонстрирует поразрядные
операции
*/
fun main(args: Array<String>){
val x: Int = 42
val y: Int = 15
val r: Int = 64
val z: Int = -1
println(“Выполнение операции x.inv()
равно ${x.inv()}”)
println(“Выполнение операции x and y
равно ${x and y}”)
println(“Выполнение операции x or y
равно ${x or y}”)
println(“Выполнение операции x xor y
равно ${x xor y}”)
println(“Выполнение операции r shl 2
равно ${r shl 2}”)
println(“Выполнение операции z ushr 24
равно ${z ushr 24}”)
}
75
Язык программирования Kotlin
ОПЕРАЦИЯ ПРИСВАИВАНИЯ
Операция присваивания обозначается одиночным знаком равенства =.
В Kotlin операция присваивания действует так же, как и во многих дру-
гих языках программирования. Она имеет следующую форму:
Переменная = выражение
В Java
int x, y, x;
X = y = z = 100;
ТЕРНАРНАЯ ОПЕРАЦИЯ
В языке Kotlin отсутствует тернарная операция как отдельный вид опе-
рации. Так как в Kotlin управляющая конструкция if является выра-
жением (возвращает значение), то она вполне справляется с функцио-
нальностью тернарный функции.
Если в Java мы пишем тернарную операцию:
То в Kotlin мы пишем:
76
Глава 4. Операции
ПРИОРИТЕТ ОПЕРАЦИЙ
Старшинство Название Символ
Prefix -, +, ++, —, !,
label
Mutiplicative *,/,%
Additive +, -
Range ..
Elvis ?:
Equality ==, !=
Conjunction &&
Disjunction ||
77
Язык программирования Kotlin
Круглые скобки
y = a shr b + 3
y = (a shr b) + 3
a or 4 + c shr b and 7
(a or (((4 + c) shr b) and 7))
ПЕРЕГРУЗКА ОПЕРАТОРОВ
В языке Kotlin реализован предопределенный набор операторов для су-
ществующих типов. Эти операторы имеют фиксированное символиче-
ское представление (вроде + или -) и фиксированные приоритеты. Для
реализации оператора Kotlin предоставляет функцию-член или функ-
цию-расширение с фиксированным именем и соответствующим типом,
78
Глава 4. Операции
-a a.unaryMinus()
!a a.not()
Инкремент и декремент
a++ a.inc()
a-- a.dec()
79
Язык программирования Kotlin
Арифметические операторы
a + b a.plus(b)
a - b a.minus(b)
a * b a.times(b)
a / b a.div(b)
a % b a.rem(b), a.mod(b)
(deprecated)
a..b a.rangeTo(b)
80
Глава 4. Операции
Оператор in
a in b b.contains(a)
a !in b !b.contains(a)
Оператор вызова
a() a.invoke()
a(i) a.invoke(i)
a(i, j) a.invoke(i, j)
a(i_1, ..., i_n) a.invoke(i_1, ..., i_n)
Составные операторы
a += b a.plusAssign(b)
a -= b a.minusAssign(b)
a *= b a.timesAssign(b)
a /= b a.divAssign(b)
a %= b a.modAssign(b)
81
Язык программирования Kotlin
Операторы равенства
a == b a?.equals(b) ?: (b === null)
a != b !(a?.equals(b) ?: (b === null))
Операторы сравнений
a > b a.compareTo(b) > 0
a < b a.compareTo(b) < 0
a >= b a.compareTo(b) >= 0
a <= b a.compareTo(b) <= 0
82
Глава 5. Управляющие операторы
ГЛАВА 5.
УПРАВЛЯЮЩИЕ
ОПЕРАТОРЫ
УПРАВЛЯЮЩИЕ ОПЕРАТОРЫ
В языках программирования управляющие операторы применяют-
ся для реализации переходов, ветвлений в потоке исполнения команд
программы, исходя из ее состояния. Управляющие операторы в языке
Kotlin можно разделить на три категории: операторы выбора, операто-
ры цикла и операторы перехода. Операторы выбора позволяют выби-
рать разные ветви выполнения команд в соответствии с результатом
вычисления заданного выражения или состояния переменной. Опера-
торы цикла позволяют повторять выполнение одного или нескольких
операторов. Операторы перехода обеспечивают возможность нелиней-
ного выполнения программы.
ОПЕРАТОРЫ ВЫБОРА
В языке Kotlin поддерживаются два оператора выбора: выражения
if и when. Эти операторы позволяют управлять порядком выполне-
ния команд программы в соответствии с условиями, которые известны
только во время выполнения.
83
Язык программирования Kotlin
ВЫРАЖЕНИЕ IF
В отличие от языка Java, где if является оператором ветвления, в языке
Kotlin if можно использовать и как оператор, и как выражение. А это,
в свою очередь, значит, что if может возвращать значение. Вследствие
этого поведение if в языке Kotlin несколько отличается от традицион-
ного поведения оператора if в других языках программирования. Об-
щая форма выражения if выглядит следующим образом:
if (условие)
оператор1
else
оператор2
84
Глава 5. Управляющие операторы
if (условие)
оператор
else if (условие)
оператор
else if (условие)
Оператор
...
...
...
85
Язык программирования Kotlin
/**
* Программа демонстрирует if-else-if
*/
fun main(args: Array<String>){
val month = 4
val season: String
if (month == 12 || month == 1 || month
== 2)
season = “зиме»
else if (month == 3 || month == 4 ||
month == 5)
season = “весне»
else if (month == 6 || month == 7 ||
month == 8)
season = “лету»
else if (month == 9 || month == 10 ||
month == 11)
season = “осени”
else
season = «вымышленным месяцам»
Println(«Указанный месяц относится
к $season»)
}
ОПЕРАТОР ?:
Если у нас есть nullable ссылка r, мы можем либо провести провер-
ку этой ссылки и использовать ее, либо использовать non-null зна-
чение x:
86
Глава 5. Управляющие операторы
val l = b?.length ?: -1
ВЫРАЖЕНИЕ WHEN
В языке Kotlin нет оператора switch. Его призвано заменить выраже-
ние when. По сути, выражение when можно назвать оператором вет-
вления. Оно представляет собой простой способ направить поток ис-
полнения команд по разным ветвям кода, в зависимости от значения
управляющего выражения. Зачастую выражение when оказывается эф-
фективнее длинных последовательностей операторов конструкции if-
else-if. Общая форма выражения when имеет следующий вид:
when (аргумент) {
Значение -> оператор
Значение -> оператор
else -> оператор
}
87
Язык программирования Kotlin
when (x) {
1 -> print(“x == 1”)
2 -> print(“x == 2”)
else -> print(“x is neither 1 nor 2”)
}
when (x) {
0, 1 -> print(“x == 0 or x == 1”)
else -> print(«otherwise»)
}
when (x) {
parseInt(s) -> print(“s encodes x”)
else -> print(“s does not encode x”)
}
when (x) {
in 1..10 -> print(“x is in the range”)
in validNumbers -> print(“x is valid”)
88
Глава 5. Управляющие операторы
when {
x.isOdd() -> print(“x is odd”)
x.isEven() -> print(“x is even”)
else -> print(“x is funny”)
}
/**
* Программа демонстрирует работу выражения
when
*/
fun main(args: Array<String>){
val month = 4
val season: String
season = when (month) {
12, 1, 2 -> “зиме”
3, 4, 5 -> “весне”
89
Язык программирования Kotlin
6, 7, 8 -> “лету”
9, 10, 11 -> “осени”
else -> «непонятно куда»
}
println(«Указанный месяц относится
к $season»)
}
ОПЕРАТОРЫ ЦИКЛА
Для управления конструкциями, которые называются циклами, в языке
Kotlin предоставляются операторы for, while и do-while. Циклы —
это конструкции, которые многократно выполняют один и тот же на-
бор инструкций до тех пор, пока не будет удовлетворено условие завер-
шения цикла.
ЦИКЛ FOR
Цикл for обеспечивает перебор всех значений, поставляемых итерато-
ром. Язык Kotlin не поддерживает традиционную форму записи цикла
for, такую, как, например, в Java:
90
Глава 5. Управляющие операторы
for (i in array.indices)
Print(array[i])
Простой цикл
for (i in 0..99) {
println(i)
}
for (i in 99 downTo 0) {
println(i)
}
91
Язык программирования Kotlin
action(index)
}
}
repeat(times = 5) {
// implicit “it” is 0..4
println(it)
}
Перебор массива
Проход по коллекции
Цикл while
while (условие) {
//..
}
92
Глава 5. Управляющие операторы
/**
* Программа проверяет, является ли число
простым
* Программа демонстрирует цикл while
*/
fun main(args: Array<String>){
val num: Int = 13
var isPrime: Boolean
var i: Int = 2
isPrime = num >= 2
while(i < num / i) {
if ((num % i) == 0) {
isPrime = false
break
}
i++
}
println(“Число $num « + (if (isPrime)
«простое» else «не простое»))
}
Цикл do-while
Тело этого цикла выполняется хотя бы один раз, поскольку его ус-
ловное выражение проверяется в конце цикла. Общая форма цикла
do-while следующая:
do {
//..
} while (условие)
93
Язык программирования Kotlin
/**
* Программа демонстрирует работу цикла do-
while
*/
fun main(args: Array<String>){
var n = 10
do {
println(“Такт №$n»)
} while (--n > 0)
}
ВЛОЖЕННЫЕ ЦИКЛЫ
Как и в других языках программирования, в Kotlin допускается при-
менение вложенных циклов. Это означает, что один цикл выполняется
в другом цикле. Следующий пример программы демонстрирует работу
вложенных циклов:
/**
* Программа демонстрирует работу цикла for
*/
fun main(args: Array<String>){
val i: Int = 0
val j: Int
for (i in 0..10) {
for (j in i..10) {
print(“.”)
}
println()
}
}
94
Глава 5. Управляющие операторы
...........
..........
.........
........
.......
......
.....
....
...
..
.
ОПЕРАТОРЫ ПЕРЕХОДА
В языке Kotlin определены три оператора перехода: break, continue
и return. Они служат для непосредственной передачи управления дру-
гой части программы.
95
Язык программирования Kotlin
fun foo() {
ints.forEach {
if (it == 0) return
print(it)
}
}
fun foo() {
ints.forEach lit@ {
if (it == 0) return@lit
print(it)
}
}
96
Глава 5. Управляющие операторы
fun foo() {
ints.forEach {
if (it == 0) return@forEach
print(it)
}
}
fun foo() {
ints.forEach(fun(value: Int) {
if (value == 0) return
print(value)
})
}
/**
* Пример программы, демонстрирующей работу
оператора break
*/
fun main(args: Array<String>){
for (i in 0..100) {
if (i == 10) break
println(“i: $i”)
}
println(“Цикл завершен оператором
97
Язык программирования Kotlin
break”)
}
/**
* Пример программы, демонстрирующей
оператор break
*/
fun main(args: Array<String>){
for (i in 0..3) {
print(“Проход $i: «)
for (j in 0..99) {
if (j == 10) break
print(“$j “)
}
println()
}
println(“Циклы завершены»)
}
98
Глава 5. Управляющие операторы
/**
* Программа демонстрирует применение
оператора continue
*/
fun main(args: Array<String>){
for (i in 0..9) {
99
Язык программирования Kotlin
print(“$i “)
if ( i % 2 == 0) continue
println()
}
}
/**
* Программа демонстрирует применение
оператора continue с меткой
*/
fun main(args: Array<String>){
loop@ for (i in 0..9) {
for ( j in 0..9 ) {
if (j > i) {
println()
continue@loop
}
print(“ “ + (i * j))
}
}
println()
}
100
Глава 5. Управляющие операторы
0 5 10 15 20 25
0 6 12 18 24 30 36
0 7 14 21 28 35 42 49
0 8 16 24 32 40 48 56 64
0 9 18 27 36 45 54 63 72 81
Оператор return
101
Язык программирования Kotlin
ГЛАВА 6.
ФУНКЦИИ И ЛЯМБДЫ
ФУНКЦИИ В KOTLIN
Основным способом оперирования данными в языке Kotlin являют-
ся функции или, как еще их называют, методы. Функция — это эле-
мент языка, который позволяет сгруппировать программный код в от-
дельную программную единицу и обращаться к ней из другого места
программы. В объектно-ориентированном программировании функ-
ции, объявления которых являются неотъемлемой частью определения
класса, называют также методами. Общая форма определения функции
имеет следующий вид:
102
Глава 6. Функции и лямбды
user_pass = readLine()
if (user_pass == valid_pass) {
println(“Пароль верный”)
} else {
println(«Пароль неверный»)
}
/*
* ...Некий длинный код и повторная
проверка
*/
if (user_pass == valid_pass) {
println(“Пароль верный”)
} else {
println(«Пароль неверный»)
}
}
103
Язык программирования Kotlin
ПРИМЕНЕНИЕ ФУНКЦИЙ
Функции в Kotlin имеют много разных форм и различные тонкости.
Ниже приведен список особенностей для функций:
• Функции одиночного выражения
• Необязательные параметры
• Позиционные и именованные аргументы
• Аргумент переменной длины
• Функциональный тип
104
Глава 6. Функции и лямбды
• Функциональный литерал
• Вызываемые ссылки (Callable references)
• Функции расширения
• Индексный вызов функций
• Локальные функции
• Замыкания
• Обобщенные функции (Generic)
• Перегрузка операторов
// Как выражение
val result = myFunction()
// Как отдельный оператор
myFinction()
105
Язык программирования Kotlin
printResult(i.toLong(), result)
}
}
MathUtils.fact()
ИНФИКСНОЕ ОБОЗНАЧЕНИЕ
Язык Kotlin предоставляет возможность вызывать функцию при помо-
щи инфиксного обозначения. Это может быть сделано, если:
• Функция является членом другой функции или расширения
• В ней используется один параметр
• Функция помечена ключевым словом infix
ПАРАМЕТРЫ ФУНКЦИИ
Как говорилось ранее, параметры функции определяются в заголовке
функции после ее имени в круглых скобках. Параметры записываются
в виде пар значений имя:тип. Параметры разделяются запятыми. Каж-
дый параметр должен быть явно указан.
106
Глава 6. Функции и лямбды
Значения по умолчанию
107
Язык программирования Kotlin
b[3] = 4
b[4] = 5
Указаны оба параметра
b[3] = 4
b[4] = 5
ИМЕНА В НАЗВАНИЯХ
ПАРАМЕТРОВ
Параметры функции могут быть названы в момент вызова функций.
Это очень удобно, когда у функции большой список параметров, в том
числе со значениями по умолчанию.
Рассмотрим такую функцию:
108
Глава 6. Функции и лямбды
divideByCamelHumps = false,
wordSeparator = ‘_’
)
ФУНКЦИИ,
ВОЗВРАЩАЮЩИЕ UNIT
Если функция не возвращает никакого полезного значения, ее возвра-
щаемый тип — Unit. Это возвращаемое значение не нуждается в яв-
ном указании:
Идентично
109
Язык программирования Kotlin
ФУНКЦИИ С ОДНИМ
ВЫРАЖЕНИЕМ
Когда функция возвращает одно-единственное выражение, круглые
скобки { } могут быть опущены и тело функции может быть описано
после знака =
ПЕРЕМЕННОЕ ЧИСЛО
АРГУМЕНТОВ
Параметр функции (обычно для этого используется последний) может
быть помечен модификатором vararg. Это позволит указать множе-
ство значений в качестве аргументов функции:
110
Глава 6. Функции и лямбды
val a = arrayOf(1, 2, 3)
val list = asList(-1, 0, *a, 4)
111
Язык программирования Kotlin
Локальные функции
class Sample() {
fun foo() { print(«Foo») }
}
112
Глава 6. Функции и лямбды
val s = Sample()
S.foo()
Функции-расширения
val l = mutableListOf(1, 2, 3)
l.swap(0, 2) // ‘this’ внутри ‘swap()’
не будет содержать значение ‘l’
113
Язык программирования Kotlin
Функции обобщения
ФУНКЦИИ С ХВОСТОВОЙ
РЕКУРСИЕЙ
Хвостовая рекурсия — частный случай рекурсии, при котором любой
рекурсивный вызов является последней операцией перед возвратом из
функции. Подобный вид рекурсии примечателен тем, что может быть
легко заменен на итерацию путем формальной и гарантированно кор-
ректной перестройки кода функции. Оптимизация хвостовой рекурсии
путем преобразования ее в плоскую итерацию реализована во многих
оптимизирующих компиляторах. В некоторых функциональных языках
программирования спецификация гарантирует обязательную оптими-
зацию хвостовой рекурсии.
Kotlin поддерживает такой стиль функционального программирова-
ния. Это позволяет использовать циклические алгоритмы вместо ре-
курсивных функций, но без риска переполнения стека. Когда функция
помечена модификатором tailrec и ее форма отвечает требованиям
компилятора, он оптимизирует рекурсию, оставляя вместо нее быстрое
и эффективное решение этой задачи, основанное на циклах.
114
Глава 6. Функции и лямбды
ЛЯМБДА-ВЫРАЖЕНИЯ
И АНОНИМНЫЕ ФУНКЦИИ
Лямбда-выражение, по сути, является анонимным методом. Но этот
метод не выполняется самостоятельно, а служит для реализации мето-
да, определяемого в функциональном интерфейсе. Нередко лямбда-вы-
ражения называют также замыканиями.
Язык Kotlin предоставляет широкие возможности по использова-
нию лямбда-выражений. Лямбда-выражения всегда заключены в фи-
гурные скобки. Лямбда-выражение определяется лямбда-оператором
или операцией (->) «стрелка».
Оператор стрелка разделяет лямбда-выражение на две части.
В левой части указываются параметры, требующиеся в лямбда- выра-
жении. Если параметры не требуются, они опускаются. В правой части
находится тело лямбда-выражения, где указываются действия, выпол-
няемые лямбда-выражением. Еще одно полезное соглашение состоит
в том, что если литерал функции имеет только один параметр, его объ-
явление может быть опущено (вместе с ->) и его имя будет it.
115
Язык программирования Kotlin
116
Глава 6. Функции и лямбды
Синтаксис лямбда-выражений
Анонимные функции
117
Язык программирования Kotlin
Замыкания
118
Глава 6. Функции и лямбды
var sum = 0
ints.filter { it > 0 }.forEach {
sum += it
}
print(sum)
1.sum(2)
class HTML {
fun body() { ... }
}
fun html(init: HTML.() -> Unit): HTML {
val html = HTML() // создание
119
Язык программирования Kotlin
объекта-приемника
html.init() // передача
приемника в лямбду
return html
}
html { // лямбда с приемником
начинается тут
body() // вызов метода объекта-
приемника
}
ВЫСОКОУРОВНЕВЫЕ ФУНКЦИИ
Высокоуровневая функция — это функция, которая принимает другую
функцию в качестве входного аргумента либо имеет функцию в каче-
стве возвращаемого результата. Хорошим примером такой функции яв-
ляется lock(), которая берет залоченный объект и функцию, приме-
няет лок, выполняет функцию и отпускает lock:
120
Глава 6. Функции и лямбды
lock (lock) {
sharedResource.operation()
}
121
Язык программирования Kotlin
ints.map { it * 2 }
Деструктуризация в лямбда-выражениях
122
Глава 6. Функции и лямбды
ВСТРОЕННЫЕ (INLINE)
ФУНКЦИИ
inline
lock(l) { foo() }
123
Язык программирования Kotlin
l.lock()
try {
foo()
}
finally {
l.unlock()
}
noinline
124
Глава 6. Функции и лямбды
Нелокальный return
fun foo() {
ordinaryFunction {
return // Ошибка: return не может
быть здесь использован
}
}
fun foo() {
inlineFunction {
return // OK: the lambda is in-
lined
}
}
125
Язык программирования Kotlin
}
return false
}
126
Глава 6. Функции и лямбды
myTree.findParentOfType(MyTreeNodeType::class.
java)
myTree.findParentOfType<MyTreeNodeType>()
myTree.findParentOfType<MyTreeNodeType>()
127
Язык программирования Kotlin
128
Глава 7. Классы и объекты
ГЛАВА 7.
КЛАССЫ И ОБЪЕКТЫ
ВВЕДЕНИЕ В КЛАССЫ
В языке Kotlin класс является одной из составляющих основ элементов
языка. Класс определяет форму и сущность объекта и образует основу
объектно-ориентированного программирования на Kotlin.
Наиболее важная особенность класса состоит в том, что он опре-
деляет новый тип данных. Как только этот новый тип данных будет
определен, им можно воспользоваться для создания объектов данно-
го типа. Таким образом, класс — это шаблон для создания объекта,
а объект — это экземпляр класса. А поскольку объект является эк-
земпляром класса, то понятия «объект» и «экземпляр» употребляют-
ся как синонимы.
129
Язык программирования Kotlin
сlass имя_класса {
Свойство1
Свойство2
...
СвойствоN
Метод1
Метод2
...
Метод N
}
ОБЪЯВЛЕНИЕ КЛАССА
Классы в Kotlin объявляются с помощью использования ключевого сло-
ва class:
class Invoice {
}
class Empty
130
Глава 7. Классы и объекты
131
Язык программирования Kotlin
boxWidth = myBox.width
132
Глава 7. Классы и объекты
Введение в методы
return значение
133
Язык программирования Kotlin
Посмотрим на строку
vol = box.volume()
134
Глава 7. Классы и объекты
КОНСТРУКТОРЫ
Инициализация всех свойств класса при каждом создании его экзем-
пляра может оказаться утомительным процессом, поэтому объектам
разрешается выполнять собственную инициализацию при их созда-
нии. Такая автоматическая инициализация осуществляется с помощью
конструкторов.
Класс в Kotlin может иметь первичный конструктор (primary
constructor) и один или более вторичный конструктор (secondary
constructors).
Первичный конструктор
135
Язык программирования Kotlin
Вторичные конструкторы
class Person {
constructor(parent: Person) {
parent.children.add(this)
}
}
136
Глава 7. Классы и объекты
СВОЙСТВА И ПОЛЯ
Как уже говорилось ранее, в языке Kotlin роль данных выполняют свой-
ства класса. Свойства в Kotlin могут быть изменяемые (mutable) и неиз-
меняемые (read-only). Для определения свойств служат ключевые сло-
ва var и val. Чтобы определить неизменяемое свойство, используется
ключевое слово val, для определения изменяемого свойства — var.
Например:
137
Язык программирования Kotlin
class ClassTest1 {
val title = “Str”
var str: String = this.title
get() = this.toString()
}
fun copyAddress(address: Address): Address {
val result = Address()
result.name = address.name
result.street = address.street
// ...
return result
}
Сначала идет ключевое слово var или val, далее имя свойства, за-
тем указывается его тип, затем инициализирующий значение и затем
методы getter и setter, которые служат для получения и установки зна-
чения свойства.
Если тип можно вывести из контекста или базового класса, то его
можно не указывать. Ели вы не собираетесь переопределять getter
и setter, их также можно не указывать. Для классов начальная иници-
ализация свойства обязательна. Например, определим изменяемые
свойства:
138
Глава 7. Классы и объекты
139
Язык программирования Kotlin
var counter = 0
set(value) {
if (value >= 0) field = value
}
140
Глава 7. Классы и объекты
_table = HashMap()
}
return _table ?: throw
AssertionError(“Set to null by another
thread”)
}
141
Язык программирования Kotlin
МЕТОДЫ И ПЕРЕГРУЗКА
МЕТОДОВ
Как уже говорилось ранее, в Kotlin для работы с данными в классах
предназначены методы. Методы — это те же самые функции, объявлен-
ные как члены класса:
class MyClass {
fun bar() {
//..
}
}
142
Глава 7. Классы и объекты
class MyClass {
protected fun bar() {
//..
}
}
Перегрузка методов
class OverloadinbgClass {
fun method(x: Int){
}
fun method(y: Long){
}
}
class OverloadingClass {
fun method(x: Int) {
println(«Вызван метод для типа Int
[$x]»)
}
143
Язык программирования Kotlin
144
Глава 7. Классы и объекты
КЛАСС STACK
Ранее в этой главе мы рассматривали класс Box, когда разбирали объ-
явление класса. Несмотря на то что класс Box удобен для демонстра-
ции основных элементов класса, его практическая польза невелика. По-
этому возьмем более сложный пример, демонстрирующий потенциал
классов. Для практического применения, изложенного выше рассмо-
трим реализацию стека. Данные хранятся в стеке по принципу: первым
пришел, последним получил пряники. В этом отношении стек можно
сравнить со стопкой тарелок: тарелка, поставленная на стол первой, бу-
дет использована последней. Для управления стеком служат две опе-
рации: размещение в стеке и извлечение из стека. Для того чтобы по-
местить элемент в стек, выполняется операция размещения, а для того
чтобы извелась элемент, нужно выполнить операцию извлечения.
145
Язык программирования Kotlin
if (tos < 0) {
println(“Стек пуст»)
return 0
}
return stck[tos--]
}
}
146
Глава 7. Классы и объекты
МОДИФИКАТОРЫ ДОСТУПА
Инкапсуляция связывает данные с манипулирующим ими кодом.
Но она также предоставляет еще одно важное средство- управление до-
ступом. Инкапсуляция позволяет управлять доступом к членам класса
из отдельных частей программы, а следовательно, предотвращать злоу-
потребления со стороны управляющего кода. Например, предоставляя
доступ к данным только с помощью ряда методов, можно предотвра-
тить злоупотребление этими данными и обеспечить защиту данных.
Способ доступа к члену класса определяется модификатором досту-
па, присутствующим в его объявлении.
Классы, объекты, интерфейсы, конструкторы, функции, свойства
и их сеттеры могут иметь модификаторы доступа (у геттеров всегда та-
кая же видимость, как у свойств, к которым они относятся). В Kotlin
предусмотрено четыре модификатора доступа: private, protected,
internal и public. Если явно не используется никакого модифика-
тора доступа, то по умолчанию применяется public. Модификаторы
доступа позволяют задать допустимую область видимости для соответ-
ствующих объектов, то есть контекст, в котором можно употреблять
данный объект.
В Kotlin используются следующие модификаторы доступа:
147
Язык программирования Kotlin
Пакеты
package foo
fun baz() {}
class Bar {}
package foo
private fun foo() {} // имеет видимость
внутри файла
public var bar: Int = 5 // свойство видно
с обратной стороны луны
private set // сеттер видно только
внутри файла
internal val baz = 6 // имеет видимость
внутри модуля
Классы и интерфейсы
148
Глава 7. Классы и объекты
149
Язык программирования Kotlin
Первичные конструкторы
Локальные объявления
Модуль
150
Глава 7. Классы и объекты
ИНТЕРФЕЙСЫ
Kotlin предоставляет возможность полностью абстрагировать интер-
фейс класса от его реализации, называемую интерфейсы. С помо-
щью этого механизма можно указать, что именно должен понять класс,
но не как это делать. Интерфейсы в Kotlin очень похожи на интерфей-
сы в Java 8. Они могут содержать абстрактные методы, методы с реали-
зацией. Главное отличие интерфейсов от абстрактных классов заключа-
ется в невозможности хранения переменных экземпляров. Они могут
иметь свойства, но те должны быть либо абстрактными, либо предо-
ставлять реализацию методов доступа. Для создания интерфейса слу-
жит ключевое слово interface:
interface MyInterface {
fun bar()
fun foo() {
// необязательное тело
}
}
151
Язык программирования Kotlin
имя_метода
}
Методы в интерфейсах
Свойства в интерфейсах
interface MyInterface {
val prop: Int // абстрактное свойство
152
Глава 7. Классы и объекты
Реализация интерфейсов
interface I1 {
fun bar()
}
interface I2 {
fun foo(): Int
}
interface II: I1, I2 {
override fun bar()
override fun foo(): Int
}
class C1: II {
override fun bar() {}
override fun foo(): Int {
//..
}
}
153
Язык программирования Kotlin
interface Callback {
fun callback(param: Int)
}
class Client: Callback {
override fun callback(param: Int){
println(“Метод callback() вызван
со значением $param”)
}
fun nonIFaceMethod() {
println(«В классах, реализующих
интерфейсы, могут определяться и другие
методы»)
}
}
fun main(args: Array<String>){
val cli = Client()
cli.callback(123)
cli.nonIFaceMethod()
}
Программа выведет:
154
Глава 7. Классы и объекты
interface Callback {
fun callback(param: Int)
}
class Client: Callback {
override fun callback(param: Int){
println(“Метод callback() вызван
со значением $param”)
}
fun nonIFaceMethod() {
println(«В классах, реализующих
интерфейсы, могут определяться и другие
методы»)
}
}
fun main(args: Array<String>){
val cli: Callback = Client()
cli.callback(123)
// cli.nonIFaceMethod() Нельзя вызвать
метод, так как в интерфейсе подобного нет
}
Частичная реализация
155
Язык программирования Kotlin
}
}
Вложенные интерфейсы
class A {
public interface NestedIF {
fun bar()
}
}
class B: A.NestedIF {
override fun bar(){
println(“Пример вложенного
интерфейса»)
}
}
fun main(args: Array<String>){
val b = B()
b.bar()
}
Переменные в интерфейсах
156
Глава 7. Классы и объекты
import java.util.Random
interface SharedConsts {
val NO get() = 1
val YES get() = 2
val MAYBE get() = 3
}
class Question: SharedConsts {
val rand = Random()
fun ask(): Int {
val prob: Int = rand.nextInt(100)
when (prob) {
in 0..33 -> return NO
in 34..67 -> return YES
else -> return MAYBE
}
}
}
class AskMe: SharedConsts {
fun answer(res: Int) {
when(res) {
NO -> println(“НЕТ»)
YES -> println(«ДА»)
else -> println(“ВОЗМОЖНО»)
}
}
}
fun main(args: Array<String>){
val q = Question()
val a = AskMe()
a.answer(q.ask())
a.answer(q.ask())
a.answer(q.ask())
a.answer(q.ask())
157
Язык программирования Kotlin
a.answer(q.ask())
}
Методы по умолчанию
interface InterfaceWithDefaultMethods {
fun bar(){
println(«Это выполнился метод
по умолчанию»)
}
}
class ClassIDM: InterfaceWithDefaultMethods
{
fun foo(){
println(“Это выполнился метод
класса”)
}
}
fun main(args: Array<String>){
val c = ClassIDM()
c.bar()
c.foo()
}
158
Глава 7. Классы и объекты
Устранение противоречий
при переопределении
interface A {
fun foo() { print(“A”) }
fun bar()
}
interface B {
fun foo() { print(“B”) }
fun bar() { print(“bar”) }
}
class C : A {
override fun bar() { print(“bar”) }
}
class D : A, B {
override fun foo() {
super<A>.foo()
super<B>.foo()
}
}
159
Язык программирования Kotlin
НАСЛЕДОВАНИЕ
Одним из основополагающих принципов объектно-ориентирован-
ного программирования является наследование, поскольку оно по-
зволяет создавать иерархические классификации. Используя насле-
дование, можно создать класс, который определяет характеристики,
общие для набора связанных элементов. Затем этот общий класс мо-
жет наследоваться другими, более специализированными класса-
ми, каждый из которых будет добавлять свои особые характеристики.
В терминологии Kotlin наследуемый класс называется суперклассом,
а наследующий — подклассом.
Чтобы наследовать класс, достаточно ввести определение суперклас-
са в определяемый класс. Рассмотрим небольшой пример:
160
Глава 7. Классы и объекты
Класс Any
class Example
Конструкторы
161
Язык программирования Kotlin
162
Глава 7. Классы и объекты
Устранение неоднозначности
open class A {
open fun f() { print(“A”) }
fun a() { print(“a”) }
}
interface B {
fun f() { print(“B”) } // interface mem-
bers are ‘open’ by default
fun b() { print(“b”) }
}
class C() : A(), B {
// The compiler requires f() to be over-
ridden:
override fun f() {
163
Язык программирования Kotlin
164
Глава 7. Классы и объекты
super.член_класса
open class A {
open val i: Int = 1
open fun show(){
println(“Функция show()
суперкласса»)
}
}
class B: A() {
override val i: Int = 2
override fun show(){
super.show()
println(“Функция show() подкласса»)
println(«Член i в суперклассе:
${super.i}»)
165
Язык программирования Kotlin
println(«Член i в подклассе:
${this.i}»)
}
}
fun main(args: Array<String>){
val c = B()
c.show()
}
АБСТРАКТНЫЕ КЛАССЫ
Иногда суперкласс требуется определить таким образом, чтобы за-
декларировать в нем структуру заданной абстракции, не предостав-
ляя полную реализацию каждого метода. Для этого в языке Kotlin слу-
жит специальный модификатор, который позволяет определять классы
и методы как абстрактные.
Класс и некоторые его члены могут быть объявлены как abstract.
Абстрактный член не имеет реализации в его классе. Обратите внима-
ние, что нам не надо аннотировать абстрактный класс или функцию
словом open — это подразумевается и так.
В языке Kotlin можно переопределить не абстрактный open член
абстрактным.
abstract class A {}
open class Base: A() {
open fun f() {}
}
abstract class Derived : Base() {
166
Глава 7. Классы и объекты
КЛАССЫ ДАННЫХ
Нередко нам приходится создавать классы, единственным назначением
которых является хранение данных. Функционал таких данных зависит
от самих данных, которые в них хранятся. Как правило, при создании
таких классов нам необходимо предусмотреть хотя бы самый мини-
мальный набор методов для работы с данными. И мы создаем такие
методы, как: equals, hashCode, toString, copy, setters
и getters. Обычно все методы во всех создаваемых data-классах де-
лают одно и то же. Как было бы круто, если бы такие рутинные зада-
чи, как создание подобных методов, взял на себя кто-то другой. Язык
Kotlin берет на себя эту обязанность, а нам взамен дает ключевое слово
167
Язык программирования Kotlin
168
Глава 7. Классы и объекты
hasCode: 995325581
toString: Customer(name=Вася, age=25)
B эквивалентен С: true
А эквивалентен В: false
А имеет значения: Customer(name=Петя, age=25)
D имеет значения: Customer(name=Петя, age=30)
Как видим из приведенного примера, нам не нужно заботиться о соз-
дании методов equals(), toString(), hashCode() и copy(). Эту
работу выполнил за нас компилятор Kotlin.
Начиная с версии 1.1 классы данных могут расширять другие классы.
Копирование
169
Язык программирования Kotlin
транслируется в код:
170
Глава 7. Классы и объекты
171
Язык программирования Kotlin
Мультидекларации
ИЗОЛИРОВАННЫЕ КЛАССЫ
Изолированные классы используются для отражения ограниченных ие-
рархий классов, когда значение может иметь тип только из ограничен-
ного набора, и никакой другой. Они являются, по сути, расширением
enum-классов: набор значений enum-типа также ограничен, но каждая
enum-константа существует только в единственном экземпляре, в то
время как наследник изолированного класса может иметь множество
экземпляров, которые могут нести в себе какое-то состояние.
Чтобы описать изолированный класс, укажите модификатор sealed
перед именем класса. Изолированный класс может иметь наследников,
но все они должны быть объявлены в том же файле, что и сам изоли-
рованный класс. Рассмотрим два примера, один для версии Kotlin 1.0
и один для версии Kotlin 1.1
// Kotlin 1.0
sealed class Car {
172
Глава 7. Классы и объекты
173
Язык программирования Kotlin
UFO speed: 0
ПЕРЕЧИСЛЕНИЯ
В простейшем виде перечисления в Kotlin похожи на перечисления
в других языках программирования, но это поверхностное сходство.
В большинстве языков вроде С++ перечисления являются списками це-
лочисленных констант. В Kotlin же перечисления определяют тип клас-
са. Благодаря тому что в Kotlin перечисления реализованы в виде клас-
сов, они могут иметь конструкторы, методы и свойства.
Перечисления создаются с помощью ключевого слова enum:
174
Глава 7. Классы и объекты
175
Язык программирования Kotlin
Применение
interface IAppleColor {
fun appleColor(): String
}
enum class Apple(val color: String): IApple-
Color {
Jonathan(“Красный»),
GoldenDel(«Желтый»),
RedDel(«Темно-красный»),
Winesap(“Красно-зеленый”),
Cortland(“Красно-желтый”)
override fun appleColor(): String {
return color
}
}
fun main(args: Array<String>){
val myApple: Apple
val goldenApple = Apple.GoldenDel
val apples: Array<Apple> = arrayOf(
Apple.Jonathan,
Apple.GoldenDel,
Apple.RedDel,
Apple.Winesap,
176
Глава 7. Классы и объекты
Apple.Cortland
)
myApple = goldenApple
val name = when (myApple) {
Apple.Jonathan -> “Джонатан»
Apple.GoldenDel -> «Гольден»
Apple.RedDel -> «Ред Делишес»
Apple.Winesap -> «Сорт Винный»
Apple.Cortland -> «Кортлэнд»
else -> «Это вообще не яблоко»
}
println(«Мое любимое яблоко $name имеет
более сладкий вкус и ${myApple.color} цвет»)
for (apple in apples) {
println(“Яблоко $apple имеет цвет
${apple.color}»)
if (apple == Apple.Winesap) {
println(“Яблоко $apple имеет
кисло-сладкий вкус»)
}
}
}
177
Язык программирования Kotlin
Ограничения
178
Глава 7. Классы и объекты
ВЛОЖЕННЫЕ КЛАССЫ
В языке Kotlin классы могут быть определены внутри других классов.
Такие классы называются вложенными. Рассмотрим небольшой пример:
class Outer {
private val bar: Int = 1
class Nested {
fun foo() = 2
}
inner class Inner {
fun foo() = bar
}
}
fun main(args: Array<String>){
println(“Значение из вложенного класса:
${Outer.Nested().foo()}”)
println(“Значение внутреннего класса:
${Outer().Inner().foo()}”)
}
Outer.Nested().foo()
Outer().Nested().foo()
179
Язык программирования Kotlin
window.addMouseListener(object: MouseAdapt-
er() {
override fun mouseClicked(e: Mou-
seEvent) {
// ...
}
ОБЪЕКТЫ
Объекты в языке Kotlin — это вполне себе определенные сущности, ко-
торые являются языковой конструкцией и призваны решить ряд опре-
деленных задач. Объекты в Kotlin создаются с помощью ключевого
слова object. В Kotlin существует три типа объектов: именованные,
анонимные и вспомогательные.
Рассмотрим общую форму создания объекта:
180
Глава 7. Классы и объекты
Именованные объекты
(объектные декларации)
object DataProviderManager {
fun registerDataProvider(provider: Data-
Provider) {
// ...
}
val allDataProviders:
Collection<DataProvider>
get() = // ...
}
DataProviderManager.registerDataProvider(...)
181
Язык программирования Kotlin
class MouseEvent
interface MouseAdapter {
fun onMouseClicked(e: MouseEvent) {
}
fun onMouseEntered(e: MouseEvent) {
}
}
object DefaultListener : MouseAdapter {
override fun onMouseClicked(e: MouseEvent)
{
// do nothing
}
override fun onMouseEntered(e: MouseEvent)
{
// do nothing
}
}
window.addMouseListener(object : MouseAdapt-
er() {
override fun mouseClicked(e: Mou-
seEvent) {
// ...
}
override fun mouseEntered(e: Mou-
seEvent) {
// ...
}
})
182
Глава 7. Классы и объекты
fun foo() {
val adHoc = object {
var x: Int = 0
var y: Int = 0
}
print(adHoc.x + adHoc.y)
}
class C {
// Приватная функция, возвращаемый тип
- анонимный объект
private fun foo() = object {
val x: String = “x”
}
// Public function, so the return type
183
Язык программирования Kotlin
is Any
fun publicFoo() = object {
val x: String = “x”
}
fun bar() {
val x1 = foo().x // Здесь все
работает
val x2 = publicFoo().x // Ошибка:
Unresolved reference ‘x’
}
}
Вспомогательные объекты
184
Глава 7. Классы и объекты
class MyClass {
companion object Factory {
fun create(): MyClass = MyClass()
}
}
class MyClass {
companion object {
}
}
val x = MyClass.Companion
interface Factory<T> {
fun create(): T
}
class MyClass {
companion object : Factory<MyClass> {
override fun create(): MyClass =
MyClass()
}
}
185
Язык программирования Kotlin
Семантическое различие
между видами объектов
ДЕЛЕГИРОВАНИЕ
Еще одной привлекательной особенностью языка Kotlin является натив-
ная поддержка такого шаблона программирования, как делегирование.
Делегирование (англ. Delegation) — основной шаблон проектирова-
ния, в котором объект внешне выражает некоторое поведение, но в ре-
альности передает ответственность за выполнение этого поведения
связанному объекту. Шаблон делегирования является фундаменталь-
ной абстракцией, на основе которой реализованы другие шаблоны —
композиция (также называемая агрегацией), примеси (mixins) и аспек-
ты (aspects).
Шаблон делегирования является хорошей альтернативой наследова-
нию, и Kotlin поддерживает его изначально, освобождая вас от необ-
ходимости написания шаблонного кода. Делегирование позволяет из-
менить поведение конкретного экземпляра объекта вместо создания
нового класса путем наследования.
interface Base {
fun print()
}
class BaseImpl(val x: Int) : Base {
override fun print() { print(x) }
}
class Derived(b: Base) : Base by b
fun main(args: Array<String>) {
val b = BaseImpl(10)
186
Глава 7. Классы и объекты
Derived(b).print() // prints 10
}
Делегированные свойства
class Example {
var p: String by Delegate()
}
187
Язык программирования Kotlin
class Delegate {
operator fun getValue(thisRef: Any?,
property: KProperty<*>): String {
return “$thisRef, спасибо
за делегирование мне ‘${property.name}’!»
}
val e = Example()
println(e.p)
e.p = «NEW»
188
Глава 7. Классы и объекты
Стандартные делегаты
189
Язык программирования Kotlin
import kotlin.properties.Delegates
class User {
var name: String by Delegates.
observable(“<no name>”) {
prop, old, new ->
println(“$old -> $new”)
}
}
fun main(args: Array<String>) {
val user = User()
user.name = “first”
user.name = “second”
}
190
Глава 7. Классы и объекты
191
Язык программирования Kotlin
192
Глава 7. Классы и объекты
class C {
var prop: Type by MyDelegate()
}
// этот код генерируется компилятором:
class C {
private val prop$delegate = MyDele-
gate()
var prop: Type
get() = prop$delegate.
getValue(this, this::prop)
set(value: Type) = prop$delegate.
setValue(this, this::prop, value)
}
Предоставление делегата
193
Язык программирования Kotlin
checkProperty(thisRef, prop.name)
// создание делегата
}
private fun checkProperty(thisRef:
MyUI, name: String) { ... }
}
fun <T> bindResource(id: ResourceID<T>):
ResourceLoader<T> { ... }
class MyUI {
val image by bindResource(ResourceID.
image_id)
val text by bindResource(ResourceID.
text_id)
}
194
Глава 7. Классы и объекты
// создание делегата
}
class C {
var prop: Type by MyDelegate()
}
// этот код будет сгенерирован компилятором
// когда функция ‘provideDelegate’ доступна:
class C {
// вызываем «provideDelegate» для
создания вспомогательного свойства «dele-
gate»
private val prop$delegate = MyDele-
gate().provideDelegate(this, this::prop)
val prop: Type
get() = prop$delegate.
getValue(this, this::prop)
}
ОБОБЩЕНИЯ
Применение обобщений позволяет создавать классы, интерфейсы и ме-
тоды, работающие безопасными по отношению к типам способом с раз-
нообразными видами данных. Многие алгоритмы логически иден-
тичны, независимо от того, к данным каких типов они применяются.
Например, механизм, поддерживающий стеки, является одним и тем же
в стеках, хранящих элементы типа Int, String, или других. Благо-
даря обобщениям можно определить алгоритм один раз, независимо
195
Язык программирования Kotlin
196
Глава 7. Классы и объекты
val t: T
val a = Gen<Int>(100)
val b = Gen<Double>(100.0)
val c = Gen<String>(“Привет»)
val a = Gen(100)
val b = Gen(100.0)
val c = Gen(«Привет»)
197
Язык программирования Kotlin
a = b
Вывод программы:
198
Глава 7. Классы и объекты
Ограниченные типы
199
Язык программирования Kotlin
// ...
}
Уточнив тип параметра типа, мы тем самым указали, что может быть
передан только тип Number или производный от него.
Тип Т ограничивается сверху классом Number, а следовательно,
компилятор теперь знает, что все объекты типа Т являются числовыми
типами и у них могут быть вызваны соответствующие методы. Но кро-
ме того, ограничение параметра типа Т предотвращает создание не чис-
ловых объектов.
По умолчанию, если не указано явно, верхней границей является
Any?.
Обобщенные методы
val l = singletonList<Int>(1)
200
Глава 7. Классы и объекты
201
Язык программирования Kotlin
if (isIn<Int>(2, nums))
println(«Число 2 содержится в массиве
nums»)
Обобщенные интерфейсы
202
Глава 7. Классы и объекты
}
fun main(args: Array<String>){
val iob = GenClass(arrayOf(3, 6, 2, 8,
6))
val cob = GenClass(arrayOf(‘b’, ‘r’,
‘p’, ‘w’))
println(“Максимум в массиве iob: ${iob.
max()}»)
println(«Минимум в массиве iob: ${iob.
min()}»)
println(«Максимум в массиве cob: ${cob.
max()}»)
println(«Минимум в массиве cob: ${cob.
min()}»)
}
203
Язык программирования Kotlin
val c = GenSubClass<Int>()
Обобщенный подкласс
204
Глава 7. Классы и объекты
Вариативность
205
Язык программирования Kotlin
Проекции типов
206
Глава 7. Классы и объекты
207
Язык программирования Kotlin
«Звездные» проекции
208
Глава 7. Классы и объекты
РАСШИРЕНИЯ
Аналогично таким языкам программирования, как C# и Gosu, Kotlin по-
зволяет расширять класс путем добавления нового функционала, не на-
следуясь от такого класса и не используя паттерн «Декоратор». Это реали-
зовано с помощью специальных выражений, называемых расширения.
Kotlin поддерживает функции-расширения и свойства-расширения.
Функции-расширения
val l = mutableListOf(1, 2, 3)
l.swap(0, 2) // ‘this’ внутри ‘swap()’
не будет содержать значение ‘l’
209
Язык программирования Kotlin
this[index2] = tmp
}
open class C
class D: C()
fun C.foo() = “c”
fun D.foo() = “d”
fun printFoo(c: C) {
println(c.foo())
}
printFoo(D())
Этот пример выведет нам “с” на экран потому, что вызванная функ-
ция-расширение зависит только от объявленного параметризованного
типа c, который является C-классом.
Если в классе есть и член в виде обычной функции, и функция-рас-
ширение с тем же возвращаемым типом, таким же именем и применя-
емая с такими же аргументами, то обычная функция имеет более высо-
кий приоритет и будет вызвана. К примеру:
class C {
fun foo() { println(“member”) }
}
fun C.foo() { println(“extension”) }
210
Глава 7. Классы и объекты
class C {
fun foo() { println(“member”) }
}
fun C.foo(i: Int) { println(“extension”) }
Свойства-расширения
class MyClass {
companion object { } // называется
211
Язык программирования Kotlin
«сompanion»
}
fun MyClass.Companion.foo() {
// ...
}
MyClass.foo()
package foo.bar
package com.example.usage
import foo.bar.goo // импортировать все
расширения за именем «goo»
// или
import foo.bar.* // импортировать все из
«foo.bar»
fun usage(baz: Baz) {
baz.goo()
)
212
Глава 7. Классы и объекты
class D {
fun bar() { ... }
}
class C {
fun baz() { ... }
fun D.foo() {
bar() // вызывает D.bar
baz() // вызывает C.baz
}
fun caller(d: D) {
d.foo() // вызов функции-
расширения
}
}
class C {
fun D.foo() {
toString() // вызывает
D.toString()
this@C.toString() // вызывает
C.toString()
}
}
open class D {
}
class D1 : D() {
}
open class C {
open fun D.foo() {
println(“D.foo in C”)
213
Язык программирования Kotlin
}
open fun D1.foo() {
println(“D1.foo in C”)
}
fun caller(d: D) {
d.foo() // вызов функции-расширения
}
}
class C1 : C() {
override fun D.foo() {
println(“D.foo in C1”)
}
override fun D1.foo() {
println(“D1.foo in C1”)
}
}
C().caller(D()) // prints “D.foo in C”
C1().caller(D()) // prints “D.foo in C1” -
получатель отсылки вычислен виртуально
C().caller(D1()) // prints “D.foo in C” -
получатель расширения вычислен статически
interface IStack<T> {
fun push(item: T)
fun pop(): T?
}
class CoolStack<T>(val size: Int = 10):
IStack<T> {
@Suppress(“UNCHECKED_CAST”)
private val stck =
arrayOfNulls<Any>(size) as Array<T>
private var tos: Int = -1
214
Глава 7. Классы и объекты
215
Язык программирования Kotlin
ГЛАВА 8.
ОБРАБОТКА ИСКЛЮЧЕНИЙ
ИСКЛЮЧЕНИЯ В KOTLIN
В этой главе рассматривается механизм обработки исключительных си-
туаций в языке Kotlin. Исключение — это ненормальная ситуация, воз-
никающая во время выполнения последовательности кода. Иными сло-
вами, это ошибка, возникающая в программе во время выполнения.
Kotlin предоставляет механизм обработки исключительных ситуаций,
который призван облегчить обработку ошибок.
216
Глава 8. Обработка исключений
try {
//.. блок кода
}
catch (исключение: тип) {
//.. обработчик исключения
}
finally {
//.. блок кода
}
КЛАССЫ ИСКЛЮЧЕНИЙ
Все исключения в Kotlin являются наследниками класса Throwable.
Это означает, что класс Throwable находится на вершине иерархии
классов исключений. Сразу же за классом Throwable ниже по иерар-
хии следуют два подкласса, разделяющие все исключения на две отдель-
ные ветки.
217
Язык программирования Kotlin
НЕОБРАБАТЫВАЕМЫЕ
ИСКЛЮЧЕНИЯ
Прежде чем перейти непосредственно к обработке исключений, имеет
смысл продемонстрировать, что происходит, когда исключение не об-
рабатываются. Ниже приведен пример программы, в которой намерен-
но введен оператор, вызывающий ошибку деления на нуль.
218
Глава 8. Обработка исключений
ОБРАБОТКА ИСКЛЮЧЕНИЙ
Стандартный обработчик исключений, предоставляемый исполняю-
щей средой, конечно же, может быть использован для отладки, но, как
правило, обрабатывать исключения приходится вручную. Это дает два
существенных преимущества. Во-первых, появляется возможность
исправить ошибку. И во-вторых, предотвращается автоматическое пре-
рывание программы.
Чтобы вручную перехватить и обработать исключительную ситу-
ацию, достаточно разместить контролируемый код в блоке оператора
try. Сразу за блоком оператора try должен идти блок catch, где ука-
зывается тип перехватываемого исключения. Ниже приведен пример,
показывающий, насколько просто это делается.
219
Язык программирования Kotlin
220
Глава 8. Обработка исключений
НЕСКОЛЬКО
ОПЕРАТОРОВ CATCH
Иногда в одном фрагменте кода может возникнуть несколько исключе-
ний. Чтобы справиться с такой ситуацией, можно указать два или бо-
лее операторов catch, каждый из которых предназначен для перехвата
отдельного типа исключения. Когда генерируется исключение, каждый
оператор catch проверяется по порядку и выполняется тот из них, ко-
торый совпадает по типу с возникшим исключением. По завершении
одного из операторов catch все остальные пропускаются и выполне-
ние программы продолжается с оператора, следующего сразу за блоком
операторов try/catch. Ниже приведен бестолковый пример програм-
мы, единственная польза от которого — продемонстрировать использо-
вание нескольких операторов catch.
221
Язык программирования Kotlin
println(“Деление на нуль: « + e)
}
catch (e: ArrayIndexOutOfBoundsExcep-
tion) {
println(“Индекс за пределами
массива: « + e)
}
println(«Жизнь после try/catch»)
}
222
Глава 8. Обработка исключений
223
Язык программирования Kotlin
ОПЕРАТОР THROW
В приведенных ранее примерах перехватывались только исключения,
которые генерировались исполняющей системой. Но исключения мож-
но генерировать и непосредственно в прикладной программе. Для это-
го служит оператор throw. Его общая форма выглядит следующим
образом:
224
Глава 8. Обработка исключений
throw генерируемое_исключение
225
Язык программирования Kotlin
Тип Nothing
ОПЕРАТОР FINALLY
Когда генерируется исключение, выполнение кода программы направ-
ляется по нелинейному пути, резко изменяющему нормальную после-
довательность выполнения операторов в коде программы. Например,
в зависимости от того, как написан метод (функция), исключение мо-
жет стать причиной преждевременного возврата из метода. В некото-
рых случаях это может вызвать серьезные осложнения. Так, если файл
открывается в начале метода и закрывается в конце, то вряд ли вас
устроит, что код, закрывающий файл, будет обойден механизмом об-
работки исключительных ситуаций. Для таких вот непредвиденных об-
стоятельств и служит оператор finally.
Оператор finally образует блок кода, который будет выпол-
нен по завершении блока операторов try/catch, но перед следую-
щим за ним кодом. Блок оператора finally выполняется независимо
от того, сгенерировано исключение или нет. Если исключение сгенери-
ровано, блок оператора finally выполнится даже при условии, что ни
один из операторов catch не совпадает с этим исключением. В любой
момент, когда происходит возврат вызывающему коду из блока опера-
торов try/catch, блок оператора finally выполняется перед возвра-
том управления.
226
Глава 8. Обработка исключений
ВСТРОЕННЫЕ ИСКЛЮЧЕНИЯ
В языке Kotlin определен ряд классов исключений, производных
от RuntimeException, которые покрывают большинство распро-
страненных ошибок вы можете использовать их в блоках try/catch.
Эти исключения являются алиасами для соответствующих типов ис-
ключений из Java.
227
Язык программирования Kotlin
СОЗДАНИЕ СОБСТВЕННЫХ
ИСКЛЮЧЕНИЙ
Встроенные исключения позволяют обрабатывать большинство рас-
пространенных ошибок. Тем не менее в прикладных программах воз-
можны особые ситуации, требующие наличия и обработки соответ-
ствующих исключений.
Для того чтобы создать класс собственного исключения, достаточ-
но определить его как производный от класса Exception, который,
в свою очередь, является наследником класса Throwable. В под-
классе собственных исключений совсем не обязательно что-нибудь реа-
лизовывать. Их присутствия в системе типов уже достаточно, чтобы их
использовать как исключения.
В приведенном ниже примере демонстрируется создание собствен-
ного класса исключения.
228
Глава 8. Обработка исключений
ЦЕПОЧКИ ИСКЛЮЧЕНИЙ
Давайте представим себе, что в методе генерируется исключение типа
ArithmeticException в связи с попыткой деления на нуль. Но ис-
тинная причина состоит в ошибке ввода-вывода, которая и приводит
к появлению неверного делителя. И хотя метод должен сгенерировать
исключение типа ArithmeticException, поскольку произошла
именно эта ошибка, тем не менее нам хотелось бы также сообщить пер-
вопричину этого безобразия. Язык Kotlin предоставляет нам такую воз-
можность, которая называется цепочки исключений.
Для организации цепочки исключений мы будет использовать метод
initCause() и свойство cause класса Throwable.
Метод initCause() связывает причину исключения с вызываю-
щим исключением и возвращает ссылку на исключение. Таким обра-
зом причину можно связать с исключением после его создания. Свой-
ство cause объекта исключения возвращает исключение, вызвавшее
текущее исключение. Пример ниже демонстрирует механизм цепочки
исключений:
229
Язык программирования Kotlin
230
Глава 9. Рефлексия и аннотации
ГЛАВА 9.
РЕФЛЕКСИЯ И АННОТАЦИИ
РЕФЛЕКСИЯ
Рефлексия — это набор возможностей языка и библиотек, который по-
зволяет интроспектировать программу (обращаться к ее структуре)
во время ее исполнения. В Kotlin функции и свойства первичны, и по-
этому их интроспекция (например, получение имени или типа во вре-
мя исполнения) сильно переплетена с использованием функциональ-
ной или реактивной парадигмы.
Ссылки на классы
val c = MyClass::class
231
Язык программирования Kotlin
Ссылки на функции
232
Глава 9. Рефлексия и аннотации
Композиция функций
Ссылки на свойства
var x = 1
fun main(args: Array<String>) {
println(::x.get()) // выведет «1»
::x.set(2)
233
Язык программирования Kotlin
Для функции-расширения:
234
Глава 9. Рефлексия и аннотации
import kotlin.reflect.jvm.*
Ссылки на конструктор
class Foo
fun function(factory : () -> Foo) {
val x : Foo = factory()
}
function(::Foo)
235
Язык программирования Kotlin
Привязанные функции
236
Глава 9. Рефлексия и аннотации
АННОТАЦИИ
В языке Kotlin поддерживается языковое свойство, позволяющее встра-
ивать справочную информацию в исходные файлы. Эта информация
называется аннотацией и не меняет порядок выполнения программы.
Это означает, что аннотация сохраняет неизменной семантику про-
граммы. Хотя аннотации не влияют на выполнение программы, тем
не менее эта информация может быть использована различными ин-
струментальными средствами на стадии разработки или развертыва-
ния прикладных программ на Kotlin.
Аннотации создаются с помощью механизма, основанного на клас-
сах, и являются специальной формой метаданных. Для объявления ан-
нотации используйте модификатор annotation перед именем класса.
Ниже приведен пример создания аннотации:
237
Язык программирования Kotlin
@Retention(правило_удержания)
@Retention(AnnotationRetention.BINARY)
annotation class MyAnnotation
Конструкторы аннотаций
238
Глава 9. Рефлексия и аннотации
import kotlin.reflect.KClass
annotation class Ann(val arg1: KClass<*>,
val arg2: KClass<out Any?>)
@Ann(String::class, Int::class) class
MyClass
239
Язык программирования Kotlin
Аннотирование лямбд
Аннотации с указаниями
@file:JvmName(«Foo»)
package org.jetbrains.demo
class Example {
@set:[Inject VisibleForTesting]
240
Глава 9. Рефлексия и аннотации
file
field
get (геттер)
set (сеттер)
param
property
field
241
Язык программирования Kotlin
@Target
CLASS
ANNOTATION_CLASS
TYPE_PARAMETER
PROPERTY
FIELD
LOCAL_VARIABLE
VALUE_PARAMETER
CONSTRUCTOR
FUNCTION
PROPERTY_GETTER
PROPERTY_SETTER
TYPE
EXPRESSION
FILE
TYPEALIAS
Встроенные аннотации
@Deprecated
242
Глава 9. Рефлексия и аннотации
@DslMarker
@ExtensionFunctionType
@ParameterName
@PublishedApi
@ReplaceWith
@SinceKotlin
@Suppress
243
Язык программирования Kotlin
@UnsafeVariance
val c = MyClass::class
Свойство annotations
import kotlin.reflect.full.memberProperties
@Retention(AnnotationRetention.RUNTIME)
annotation class MyAnno(val src: String)
@MyAnno(“Это аннотация для класса”) class
MyAnnoClass {
@MyAnno(«Это аннотация для свойства»)
val property = 1
244
Глава 9. Рефлексия и аннотации
}
fun main(args: Array<String>){
val c = MyAnnoClass()
val m = c::class
val p = m.memberProperties
val a = m.findAnnotation<MyAnno>()
println(m.annotations)
p.map {
println(it.annotations)
}
println(a)
}
Функция-расширение findAnnotation()
val m = c::class
val a = m.findAnnotation<MyAnno>()
245
Язык программирования Kotlin
ГЛАВА 10.
СОПРОГРАММЫ
ВВЕДЕНИЕ В СОПРОГРАММЫ
246
Глава 10. Сопрограммы
247
Язык программирования Kotlin
Функции приостановки
248
Глава 10. Сопрограммы
async {
doSomething(foo)
...
}
async {
...
val result = await(...)
...
}
interface Base {
suspend fun foo()
}
class Derived: Base {
override suspend fun foo() { ... }
}
Аннотация @RestrictsSuspension
249
Язык программирования Kotlin
@RestrictsSuspension
public abstract class SequenceBuilder<in T>
{
...
}
250
Глава 10. Сопрограммы
Низкоуровневый API
Генераторы
buildSequence()
buildIterator()
import kotlin.coroutines.experimental.*
fun main(args: Array<String>) {
//sampleStart
val fibonacciSeq = buildSequence {
var a = 0
var b = 1
yield(1)
251
Язык программирования Kotlin
while (true) {
yield(a + b)
val tmp = a + b
a = b
b = tmp
}
}
//sampleEnd
// Print the first five Fibonacci numbers
println(fibonacciSeq.take(8).toList())
}
import kotlin.coroutines.experimental.*
fun main(args: Array<String>) {
//sampleStart
val lazySeq = buildSequence {
print(“START “)
for (i in 1..5) {
yield(i)
print(“STEP “)
}
print(“END”)
}
// Print the first three elements of the
sequence
lazySeq.take(3).forEach { print(“$it “)
252
Глава 10. Сопрограммы
}
//sampleEnd
}
import kotlin.coroutines.experimental.*
fun main(args: Array<String>) {
//sampleStart
val lazySeq = buildSequence {
yield(0)
yieldAll(1..10)
}
lazySeq.forEach { print(“$it “) }
//sampleEnd
}
import kotlin.coroutines.experimental.*
//sampleStart
suspend fun SequenceBuilder<Int>.
yieldIfOdd(x: Int) {
if (x % 2 != 0) yield(x)
}
val lazySeq = buildSequence {
for (i in 1..10) yieldIfOdd(i)
253
Язык программирования Kotlin
}
//sampleEnd
fun main(args: Array<String>) {
lazySeq.forEach { print(“$it “) }
}
Сопрограммы: выводы
254
Глава 10. Сопрограммы
255
Язык программирования Kotlin
ГЛАВА 11.
КОЛЛЕКЦИИ
ВВЕДЕНИЕ
Язык Kotlin, как и многие другие языки программирования, реализует
прикладной интерфейс для работы с коллекциями.
Коллекция — программный объект, содержащий в себе, тем или
иным образом, набор значений одного или различных типов и позво-
ляющий обращаться к этим значениям.
Коллекция дает возможность записывать в себя значения и извле-
кать их. Назначение коллекции — служить хранилищем объектов и обе-
спечивать доступ к ним. Обычно коллекции используются для хране-
ния групп однотипных объектов, подлежащих стереотипной обработке.
Для обращения к конкретному элементу коллекции могут использо-
ваться различные методы, в зависимости от ее логической организа-
ции. Реализация может допускать выполнение отдельных операций над
коллекциями в целом. Наличие операций над коллекциями во многих
случаях способно существенно упростить программирование.
Прикладной интерфейс для работы с коллекциями в Kotlin разрабо-
тан для достижения нескольких целей:
256
Глава 11. Коллекции
COLLECTION
И MUTABLECOLLECTION
Общая коллекция элементов. Методы в этом интерфейсе поддержива-
ют доступ только для чтения к коллекции. Доступ для чтения / записи
поддерживается через интерфейс MutableCollection.
257
Язык программирования Kotlin
258
Глава 11. Коллекции
259
Язык программирования Kotlin
260
Глава 11. Коллекции
261
Язык программирования Kotlin
262
Глава 11. Коллекции
263
Язык программирования Kotlin
264
Глава 11. Коллекции
265
Язык программирования Kotlin
266
Глава 11. Коллекции
LIST И MUTABLELIST
Общий упорядоченный набор элементов. Методы в этом интерфейсе
поддерживают только доступ для чтения к списку. Доступ для чтения /
записи поддерживается через интерфейс MutableList.
267
Язык программирования Kotlin
SET И MUTABLESET
Общий неупорядоченный набор элементов, который не поддерживает
повторяющиеся элементы. Методы в этом интерфейсе поддерживают
доступ только для чтения к набору; Доступ для чтения / записи поддер-
живается через интерфейс MutableSet.
ИСПОЛЬЗОВАНИЕ КОЛЛЕКЦИЙ
Тип List<out T> в Kotlin — интерфейс, который предоставляет read-
only операции, такие как size, get и другие. Так же как и в Java, он на-
следуется от Collection<T>, а значит, и от Iterable<T>. Методы,
которые изменяют список, добавлены в интерфейс MutableList<T>.
То же самое относится и к Set<out T>/MutableSet<T>, Map<K,
out V>/MutableMap<K, V>.
Ниже приведен пример базового использования списка (List)
и множества (Set):
268
Глава 11. Коллекции
println(readOnlyView)
val strings = hashSetOf(“a”, “b”, “c”,
“c”)
assert(strings.size == 3)
println(strings)
}
Методы
hasNext Возвращает true, если итерация имеет
больше элементов.
269
Язык программирования Kotlin
Функции-расширения
asSequence Создает последовательность, кото-
рая возвращает все элементы из этого
итератора. Последовательность огра-
ничена повторением только один раз.
270
Глава 11. Коллекции
num.forEach {
print(it)
}
num.map {
print(it)
}
MAP И MUTABLEMAP
Отображения
Map и MutableMap
271
Язык программирования Kotlin
Методы Map
containsKey Возвращает true, если Map содержит
указанный ключ.
containsValue Возвращает true, если Map содержит
один или несколько ключей с указанным
значением.
get Возвращает значение, соответствующее
заданному ключу, или null, если такой
ключ отсутствует на Map.
getOrDefault Возвращает значение, соответствующее
данному ключу, или значение по умол-
чанию, если такой ключ отсутствует
в Map.
isEmpty Возвращает true, если Map не содер-
жит элементов, в противном случае
- false.
Методы MutableMap
clear Удаляет все элементы
put Связывает указанное значение с ука-
занным ключом
putAll Обновляет Map с помощью пар ключ /
значение из указанной Map.
remove Удаляет указанный ключ и его соответ-
ствующее значение из Map.
272
Глава 11. Коллекции
273
Язык программирования Kotlin
274
Глава 11. Коллекции
275
Язык программирования Kotlin
276
Глава 12. Другие особенности языка
ГЛАВА 12.
ДРУГИЕ ОСОБЕННОСТИ
ЯЗЫКА
277
Язык программирования Kotlin
ИНТЕРВАЛЫ
В языке Kotlin реализована очень полезная функциональность под
названием интервалы. Интервалы оформлены с помощью функций
rangeTo() и имеют оператор в виде (..), который дополняется
in и !in. Они применимы ко всем сравниваемым (comparable) типам.
В общем виде интервал может быть представлен следующим образом:
278
Глава 12. Другие особенности языка
IntProgression.fromClosedRange(start, end,
increment)
Вспомогательные функции
rangeTo()
279
Язык программирования Kotlin
class Int {
//...
operator fun rangeTo(other: Long): Lon-
gRange = LongRange(this, other)
//...
operator fun rangeTo(other: Int): In-
tRange = IntRange(this, other)
//...
}
downTo()
280
Глава 12. Другие особенности языка
reversed()
step()
281
Язык программирования Kotlin
Элемент last
282
Глава 12. Другие особенности языка
NULL-БЕЗОПАСНОСТЬ
Система типов в языке Kotlin нацелена на то, чтобы искоренить опас-
ность обращения к null значениям, более известную как «Ошибка
на миллион».
Самым распространенным подводным камнем многих языков про-
граммирования, в том числе Java, является попытка произвести доступ
к null значению. Это приводит к ошибке. В Java такая ошибка называ-
ется NullPointerException (сокр. «NPE»).
Язык Kotlin призван исключить ошибки подобного рода. NPE могут
возникать только в случае:
• Явного указания throw NullPointerException()
• Использования оператора !!
• Эту ошибку вызвал внешний код
• Есть какое-то несоответствие при инициализации данных
(в конструкторе использована ссылка this на данные, которые
не были еще проинициализированы)
val l = a.length
283
Язык программирования Kotlin
Проверка на null
Безопасные вызовы
val l = b?.length
284
Глава 12. Другие особенности языка
bob?.department?.head?.name
Такая цепочка вернет null в случае, если одно из свойств имеет зна-
чение null.
Для проведения каких-либо операций над non-null значениями вы
можете использовать let оператор вместе с оператором безопасного
вызова:
Элвис-оператор
val l = b?.length ?: -1
285
Язык программирования Kotlin
Оператор !!
val l = b!!.length
В случае если вам нужен NPE, вы можете заполучить его только пу-
тем явного указания.
286
Глава 13. Грамматика языка
ГЛАВА 13.
ГРАММАТИКА ЯЗЫКА
ГРАММАТИКА
В этом разделе представлена грамматическая нотация языка Kotlin.
Syntax
start
kotlinFile
: preamble topLevelObject*
;
start
script
: preamble expression*
;
preamble (used by script, kotlinFile)
: fileAnnotations? packageHeader? import*
;
fileAnnotations (used by preamble)
: fileAnnotation*
;
fileAnnotation (used by fileAnnotations)
: “@” “file” “:” (“[“ unescapedAnnotation+ “]”
| unescapedAnnotation)
;
packageHeader (used by preamble)
: modifiers “package” SimpleName{“.”} SEMI?
287
Язык программирования Kotlin
;
import (used by preamble)
: “import” SimpleName{“.”} (“.” “*” | “as”
SimpleName)? SEMI?
;
288
Глава 13. Грамматика языка
289
Язык программирования Kotlin
function)
: “(“ functionParameter{“,”}? “)”
;
functionParameter (used by valueParameters, pri-
maryConstructor)
: modifiers (“val” | “var”)? parameter (“=”
expression)?
;
block (used by catchBlock, anonymousInitializer,
secondaryConstructor, functionBody, controlStruc-
tureBody, try, finallyBlock)
: “{“ statements “}”
;
function (used by memberDeclaration, declara-
tion, topLevelObject)
: modifiers “fun”
typeParameters?
(type “.”)?
SimpleName
typeParameters? valueParameters (“:”
type)?
typeConstraints
functionBody?
;
functionBody (used by getter, setter, function)
: block
: “=” expression
;
variableDeclarationEntry (used by for, lambdaPa-
rameter, property, multipleVariableDeclarations)
: SimpleName (“:” type)?
;
multipleVariableDeclarations (used by for, lamb-
daParameter, property)
: “(“ variableDeclarationEntry{“,”} “)”
;
property (used by memberDeclaration, declara-
tion, topLevelObject)
: modifiers (“val” | “var”)
typeParameters?
(type “.”)?
290
Глава 13. Грамматика языка
(multipleVariableDeclarations | vari-
ableDeclarationEntry)
typeConstraints
(“by” | “=” expression SEMI?)?
(getter? setter? | setter? getter?) SEMI?
;
getter (used by property)
: modifiers “get”
: modifiers “get” “(“ “)” (“:” type)? func-
tionBody
;
setter (used by property)
: modifiers “set”
: modifiers “set” “(“ modifiers (SimpleName |
parameter) “)” functionBody
;
parameter (used by functionType, setter, func-
tionParameter)
: SimpleName “:” type
;
object (used by memberDeclaration, declaration,
topLevelObject)
: “object” SimpleName primaryConstructor? (“:”
delegationSpecifier{“,”})? classBody?
secondaryConstructor (used by memberDeclaration)
: modifiers “constructor” valueParameters (“:”
constructorDelegationCall)? block
;
constructorDelegationCall (used by secondaryCon-
structor)
: “this” valueArguments
: “super” valueArguments
;
Enum classes
enumClassBody (used by class)
: “{“ enumEntries (“;” members)? “}”
;
enumEntries (used by enumClassBody)
: (enumEntry{“,”} “,”? “;”?)?
;
enumEntry (used by enumEntries)
291
Язык программирования Kotlin
292
Глава 13. Грамматика языка
: block
: blockLevelExpression
;
if (used by atomicExpression)
: “if” “(“ expression “)” controlStructureBody
SEMI? (“else” controlStructureBody)?
;
try (used by atomicExpression)
: “try” block catchBlock* finallyBlock?
;
catchBlock (used by try)
: “catch” “(“ annotations SimpleName “:”
userType “)” block
;
finallyBlock (used by try)
: “finally” block
;
loop (used by atomicExpression)
: for
: while
: doWhile
;
for (used by loop)
: “for” “(“ annotations (multipleVari-
ableDeclarations | variableDeclarationEntry)
“in” expression “)” controlStructureBody
;
while (used by loop)
: “while” “(“ expression “)” controlStructure-
Body
;
doWhile (used by loop)
: “do” controlStructureBody “while” “(“ ex-
pression “)”
;
Expressions
Precedence
Precedence Title Symbols
Highest Postfix ++, --, ., ?., ?
Prefix -, +, ++, --, !, labelDefinition@
Type RHS :, as, as?
293
Язык программирования Kotlin
Multiplicative *, /, %
Additive +, -
Range ..
Infix function SimpleName
Elvis ?:
Named checks in, !in, is, !is
Comparison <, >, <=, >=
Equality ==, \!==
Conjunction &&
Disjunction ||
Lowest Assignment =, +=, -=, *=, /=, %=
Rules
expression (used by for, atomicExpression, long-
Template, whenCondition, functionBody, doWhile,
property, script, explicitDelegation, jump,
while, arrayAccess, blockLevelExpression, if,
when, valueArguments, functionParameter)
: disjunction (assignmentOperator disjunc-
tion)*
;
disjunction (used by expression)
: conjunction (“||” conjunction)*
;
conjunction (used by disjunction)
: equalityComparison (“&&” equalityCompari-
son)*
;
equalityComparison (used by conjunction)
: comparison (equalityOperation comparison)*
;
comparison (used by equalityComparison)
: namedInfix (comparisonOperation namedInfix)*
;
namedInfix (used by comparison)
: elvisExpression (inOperation elvisExpres-
sion)*
: elvisExpression (isOperation type)?
;
elvisExpression (used by namedInfix)
: infixFunctionCall (“?:” infixFunctionCall)*
;
294
Глава 13. Грамматика языка
295
Язык программирования Kotlin
: try
: objectLiteral
: jump
: loop
: SimpleName
;
labelReference (used by atomicExpression, jump)
: “@” ++ LabelName
;
labelDefinition (used by prefixUnaryOperation, an-
notatedLambda)
: LabelName ++ “@”
;
literalConstant (used by atomicExpression)
: “true” | “false”
: stringTemplate
: NoEscapeString
: IntegerLiteral
: HexadecimalLiteral
: CharacterLiteral
: FloatLiteral
: “null”
;
stringTemplate (used by literalConstant)
: “\”” stringTemplateElement* “\””
;
stringTemplateElement (used by stringTemplate)
: RegularStringPart
: ShortTemplateEntryStart (SimpleName |
“this”)
: EscapeSequence
: longTemplate
;
longTemplate (used by stringTemplateElement)
: “${“ expression “}”
;
declaration (used by statement)
: function
: property
: class
: typeAlias
296
Глава 13. Грамматика языка
: object
;
statement (used by statements)
: declaration
: blockLevelExpression
;
blockLevelExpression (used by statement, con-
trolStructureBody)
: annotations (“\n”)+ expression
;
multiplicativeOperation (used by multiplicative-
Expression)
: “*” : “/” : “%”
;
additiveOperation (used by additiveExpression)
: “+” : “-”
;
inOperation (used by namedInfix)
: “in” : “!in”
;
typeOperation (used by typeRHS)
: “as” : “as?” : “:”
;
isOperation (used by namedInfix)
: “is” : “!is”
;
comparisonOperation (used by comparison)
: “<” : “>” : “>=” : “<=”
;
equalityOperation (used by equalityComparison)
: “!=” : “==”
;
assignmentOperator (used by expression)
: “=”
: “+=” : “-=” : “*=” : “/=” : “%=”
;
prefixUnaryOperation (used by prefixUnaryExpres-
sion)
: “-” : “+”
: “++” : “--”
: “!”
297
Язык программирования Kotlin
: annotations
: labelDefinition
;
postfixUnaryOperation (used by postfixUnaryExpres-
sion)
: “++” : “--” : “!!”
: callSuffix
: arrayAccess
: memberAccessOperation postfixUnaryExpression
;
callSuffix (used by constructorInvocation, post-
fixUnaryOperation)
: typeArguments? valueArguments annotatedLamb-
da
: typeArguments annotatedLambda
;
annotatedLambda (used by callSuffix)
: (“@” unescapedAnnotation)* labelDefinition?
functionLiteral
memberAccessOperation (used by postfixUnaryOpera-
tion)
: “.” : “?.” : “?”
;
typeArguments (used by callSuffix, callableRefer-
ence, unescapedAnnotation)
: “<” type{“,”} “>”
;
valueArguments (used by callSuffix, construc-
torDelegationCall, unescapedAnnotation)
: “(“ (SimpleName “=”)? “*”? expression{“,”}
“)”
;
jump (used by atomicExpression)
: “throw” expression
: “return” ++ labelReference? expression?
: “continue” ++ labelReference?
: “break” ++ labelReference?
;
functionLiteral (used by atomicExpression, anno-
tatedLambda)
: “{“ statements “}”
298
Глава 13. Грамматика языка
299
Язык программирования Kotlin
: (modifier | annotations)*
;
typeModifiers (used by type)
: (suspendModifier | annotations)*
;
modifier (used by modifiers)
: classModifier
: accessModifier
: varianceAnnotation
: memberModifier
: parameterModifier
: typeParameterModifier
: functionModifier
: propertyModifier
;
classModifier (used by modifier)
: “abstract”
: “final”
: “enum”
: “open”
: “annotation”
: “sealed”
: “data”
;
memberModifier (used by modifier)
: “override”
: “open”
: “final”
: “abstract”
: “lateinit”
;
accessModifier (used by modifier)
: “private”
: “protected”
: “public”
: “internal”
;
varianceAnnotation (used by modifier, optional-
Projection)
: “in”
: “out”
300
Глава 13. Грамматика языка
;
parameterModifier (used by modifier)
: “noinline”
: “crossinline”
: “vararg”
;
typeParameterModifier (used by modifier)
: “reified”
;
functionModifier (used by modifier)
: “tailrec”
: “operator”
: “infix”
: “inline”
: “external”
: suspendModifier
;
propertyModifier (used by modifier)
: “const”
;
suspendModifier (used by typeModifiers, function-
Modifier)
: “suspend”
;
Annotations
annotations (used by catchBlock, prefixUnaryOper-
ation, blockLevelExpression, for, typeModifiers,
class, modifiers, typeConstraint)
: (annotation | annotationList)*
;
annotation (used by annotations)
: “@” (annotationUseSiteTarget “:”)? unes-
capedAnnotation
;
annotationList (used by annotations)
: “@” (annotationUseSiteTarget “:”)? “[“ unes-
capedAnnotation+ “]”
;
annotationUseSiteTarget (used by annotation, an-
notationList)
: “field”
301
Язык программирования Kotlin
: “file”
: “property”
: “get”
: “set”
: “receiver”
: “param”
: “setparam”
: “delegate”
;
unescapedAnnotation (used by annotation, fileAn-
notation, annotatedLambda, annotationList)
: SimpleName{“.”} typeArguments? valueArgu-
ments?
;
Lexical structure
helper
Digit (used by IntegerLiteral, HexDigit)
: [“0”..”9”];
IntegerLiteral (used by literalConstant)
: Digit (Digit | “_”)*
FloatLiteral (used by literalConstant)
: <Java double literal>;
helper
HexDigit (used by RegularStringPart, Hexadeci-
malLiteral)
: Digit | [“A”..”F”, “a”..”f”];
HexadecimalLiteral (used by literalConstant)
: “0x” HexDigit (HexDigit | “_”)*;
CharacterLiteral (used by literalConstant)
: <character as in Java>;
NoEscapeString (used by literalConstant)
: <”””-quoted string>;
RegularStringPart (used by stringTemplateEle-
ment)
: <any character other than backslash, quote,
$ or newline>
ShortTemplateEntryStart:
: “$”
EscapeSequence:
: UnicodeEscapeSequence | RegularEscapeSe-
quence
302
Глава 13. Грамматика языка
UnicodeEscapeSequence:
: “\u” HexDigit{4}
RegularEscapeSequence:
: “\” <any character other than newline>
SEMI (used by whenEntry, if, statements, pack-
ageHeader, property, import)
: <semicolon or newline>;
SimpleName (used by typeParameter, catchBlock,
simpleUserType, atomicExpression, LabelName,
packageHeader, class, object, infixFunctionCall,
function, typeAlias, parameter, callableRefer-
ence, variableDeclarationEntry, stringTemplateEl-
ement, enumEntry, setter, import, companionOb-
ject, valueArguments, unescapedAnnotation,
typeConstraint)
: <java identifier>
: “`” <java identifier> “`”
;
LabelName (used by labelReference, labelDefini-
tion)
: “@” SimpleName;
303
Учебная литература
Сергей Пименов
Язык программирования
Kotlin
Литературный редактор С. Альперт
Дизайн обложки С. Пименов
Верстка В. Мартыновский