Краткое содержание книги «Kotlin in Action» — часть 1

В этой серии статей мы рассмотрим наиболее важные темы книги «Kotlin in Action». Поскольку это всего лишь краткое изложение книги без подробностей, и чтобы получить максимальную пользу от этой серии, я настоятельно рекомендую вам прочитать книгу, а после прочтения каждой главы прочитать соответствующую статью для обзора концепций и материала. Эта серия также отлично подходит для разработчиков, которые готовятся к собеседованию. Поскольку глава 1 книги содержит только общую информацию, мы пропустим эту главу и начнем со второй главы книги. Я буду указывать номер страницы после названия каждого раздела, чтобы вы могли прочитать концепцию, если она не ясна по статье. Давайте погрузимся внутрь.

Если вы уже читали эту статью или считаете, что вам не нужно читать эту главу, вы можете прочитать часть 2 здесь.

Функции

страницы 18, 19

  1. Чтобы объявить функцию, мы используем ключевое слово fun.
  2. Функции могут быть объявлены на верхнем уровне файла (вне класса).
  3. Типы параметров записываются после имени параметра через двоеточие.
  4. Тип возврата функции записывается после скобок параметров через двоеточие.
  5. Если функция является блочным выражением, то тип возврата и фигурные скобки можно опустить (блочное выражение будет рассмотрено позже).

Утверждения и выражения

страница 19

Выражение имеет значение, которое может быть использовано как часть другого выражения.
Выражение является элементом верхнего уровня в своем объемлющем блоке и не имеет собственного значения.
Большинство утверждений Java можно использовать в качестве выражений в Kotlin (if, try, when). Поскольку if может использоваться как выражение в Kotlin, у нас нет троичного оператора(int k = booleanVariable ? 1 : 0;), который есть в Java.
Пожалуйста, имейте в виду, что if в Kotlin может использоваться как выражение или утверждение. Если оператор if охватывает все случаи и присваивается переменной, то это выражение. В противном случае это выражение.

Тело выражения и тело блока

страница 19

Если функция возвращает значение напрямую, без использования фигурных скобок, то она имеет тело выражения. С другой стороны, если функция имеет фигурные скобки, она имеет тело блока.

Переменные

страницы 20, 21

В отличие от Java, Kotlin не требует начинать переменную с ее типа, потому что в некоторых случаях мы можем опустить тип переменной (используя возможности компилятора Kotlin по выводу типов).
Для объявления переменных в Kotlin мы используем val или var. val — это сокращение для значения, а var — сокращение для переменной. Как следует из названий, val является неизменяемым, а var — изменяемым.
ПРИМЕЧАНИЕ: Мы всегда должны использовать ключевое слово val и использовать var, если нам действительно нужно изменить переменную, тогда мы должны использовать var.
ПРИМЕЧАНИЕ: Хотя val создает неизменяемую переменную, имейте в виду, что сам объект может быть изменяемым.

val s = arrayListOf("hi")
s.add("hello")
Вход в полноэкранный режим Выход из полноэкранного режима

Шаблоны строк

страница 22

Kotlin поддерживает шаблоны строк. Мы можем объявить переменную, а затем использовать ее вместе со знаком доллара ($), чтобы упомянуть ее в строках.

val appleNumbers = 1100
println("there are $appleNumbers in the inventory")
Вход в полноэкранный режим Выйти из полноэкранного режима

ПРИМЕЧАНИЕ: Если мы хотим использовать в строке сам знак доллара, его следует экранировать:

println("$x")
Войти в полноэкранный режим Выйти из полноэкранного режима

Это выведет $x, а не значение переменной с именем x.

Классы и свойства

страницы 23, 24, 25
class Person {
    val name: String,
    var isMarried: Boolean
Войти в полноэкранный режим Выход из полноэкранного режима

Классы, которые содержат только данные и никакого другого кода, называются объектами значений.

В Kotlin модификатором видимости по умолчанию является public.

В Java комбинацию поля и его аксессоров часто называют свойством. В Kotlin свойства поддерживаются языком и заменяют поля и методы-аксессоры.
Когда мы объявляем свойство как val, для него генерируется поле и геттер. В то время как для свойств var генерируется поле, геттер и сеттер.

Когда мы вызываем свойство объекта, кажется, что мы обращаемся к нему напрямую, но под капотом мы обращаемся к нему через сгенерированный метод getter.

Боб

верно

Бывают случаи, когда нам не нужно базовое поле для свойства. Например, если мы можем вычислить свойство из значения других свойств. Мы можем объявить пользовательский getter для такого свойства, и для него не будет создаваться вспомогательное поле.

class Rectangle(val height: Int, val width: Int) {
    val isSquare: Boolean
        get() {
            return height == width
        }
}
Вход в полноэкранный режим Выход из полноэкранного режима

В приведенном выше коде компилятор не будет генерировать подложку для свойства isSquare.

Энумы

страницы 28, 29
enum class Color {
    RED, BLUE, YELLOW, GREEN
}
Вход в полноэкранный режим Выход из полноэкранного режима

Enum — это редкий случай в Kotlin, который использует больше ключевых слов, чем эквивалент в Java.

В Kotlin, enum — это мягкое ключевое слово. Оно имеет особое значение, когда идет перед class, но мы можем использовать его и в других местах нашего кода.

Перечисления — это не просто список значений. Мы можем объявлять методы и свойства в перечислениях.

enum class Color(
  val r: Int, val g: Int, val b: Int
) {
  RED(255, 0, 0), BLUE(0, 0, 255), YELLOW(255, 255, 0), 
    GREEN(0, 255, 0);

  fun rgb() = (r * 256 + g) * 256 + b
}
Вход в полноэкранный режим Выход из полноэкранного режима
  1. В конструкторе перечисления мы объявили свойства констант перечисления.
  2. Точка с запятой требуется, если мы объявляем методы в перечислении.

Когда

страницы 29, 30, 31

Как и if, when — это выражение, которое возвращает значение.
В отличие от Java, when не требует писать break после каждого случая.

fun getWarmth(color: Color) =
  when (color) {
    Color.RED -> "warm"
    Color.BLUE -> "cold"
    Color.YELLOW -> "warm"
    Color.GREEN -> "neutral"
  }

Вход в полноэкранный режим Выход из полноэкранного режима

Мы можем объединить несколько значений в одной ветке, разделенных комой(,).

fun getWarmth(color: Color) =
  when (color) {
    Color.RED, Color.YELLOW -> "warm"
    Color.BLUE -> "cold"
    Color.GREEN -> "neutral"
  }

Войти в полноэкранный режим Выход из полноэкранного режима

В отличие от switch в Java, который требует, чтобы мы использовали константы (перечисления, строки или числовые литералы) в качестве условий ветвления, when в Kotlin позволяет нам использовать любые объекты.

fun mix(c1: Color, c2: Color) =
  when(setOf(c1, c2)) {
    setOf(RED, YELLOW) -> ORANGE
    setOf(BLUE, YELLOW) -> GREEN
    setOf(BLUE, VIOLET) -> INDIGO
    else -> throw Exception("Dirty color")
  }
}
Войти в полноэкранный режим Выйти из полноэкранного режима

Здесь мы использовали Set в качестве условия ветвления.

Предыдущий пример неэффективен, поскольку создает набор для каждого вызова. Вместо этого мы можем использовать when без аргумента для достижения того же результата:

fun mixOptimized(c1: Color, c2: Color) =
  when {
    (c1 == RED && c2 = YELLOW) || 
    (c1 == YELLOW && c2 = RED)  -> ORANGE

    (c1 == BLUE && c2 = YELLOW) || 
    (c1 == YELLOW && c2 = BLUE) -> GREEN

    (c1 == BLUE && c2 = VIOLET) || 
    (c1 == VIOLET && c2 = BLUE) -> GREEN -> INDIGO
    else -> throw Exception("Dirty color")
  }
}
Войти в полноэкранный режим Выйти из полноэкранного режима

ПРИМЕЧАНИЕ: Если для выражения when не задан аргумент, условием ветвления является любое булево выражение.

Интеллектуальные касты

страницы 31, 32, 33

Чтобы проиллюстрировать механизм кастинга в Kotlin, мы воспользуемся примером с арифметическим выражением. В этом примере мы будем использовать только одну операцию: сумму двух чисел. Давайте погрузимся в работу:
Сначала мы объявляем интерфейс, чтобы иметь один и тот же тип для значения и выражения двух значений. Затем мы реализуем этот интерфейс в двух классах Sum и Num.

interface Expr
class Num(val value: Int) : Expr
class Sum(val left: Expr, val right: Expr) : Expr
Вход в полноэкранный режим Выход из полноэкранного режима

ПРИМЕЧАНИЕ: Чтобы реализовать интерфейс в классе, мы используем двоеточие после имени класса, а затем имя интерфейса:

Интерфейс Expr имеет две реализации, поэтому у нас есть два варианта:

  1. Если выражение является числом, мы возвращаем соответствующее значение.
  2. Если это сумма, мы должны оценить левое и правое выражения и вернуть их сумму.

Сначала рассмотрим простую реализацию:

fun eval(e: Expr) : Int {
  if (e is Num) {
    val n = e as Num
    return n.value
  }
  if (e is Sum) {
    return eval(e.right) + eval(e.left)
  }
  throw IllegalArgumentException("unknown expression")
}
Вход в полноэкранный режим Выход из полноэкранного режима

is vs instanceof
Проверка is в Kotlin аналогична проверке instanceof в Java. В Java, если мы проверили переменную с помощью instanceof, мы также должны привести ее к типу. Однако в Kotlin, если мы проверяем переменную с помощью проверки is, она уже может быть использована в качестве типа, который мы проверили с помощью проверки is. По сути, компилятор выполняет приведение за нас (что называется умным приведением).

ПРИМЕЧАНИЕ: Умное приведение работает только в том случае, если переменная не могла измениться после проверки is. Для свойства класса оно должно быть val или не иметь пользовательского аксессора!

Поскольку в Kotlin есть возможность умного приведения, мы можем удалить приведение в приведенном выше коде.
Мы также можем убрать фигурные скобки и ключевые слова return, потому что в Kotlin if — это выражение.
Результатом двух приведенных выше модификаций будет следующее:

fun eval(e: Expr) =
  if (e is Num) {
    e.value
  } else if (e is Sum) {
    eval(e.right) + eval(e.left)
  } else
  throw IllegalArgumentException("unknown expression")
Вход в полноэкранный режим Выйти из полноэкранного режима

ПРИМЕЧАНИЕ: И if, и when могут иметь блоки. В этом случае последнее выражение в блоке является результатом.

Мы также можем заменить ветви if на when:

fun eval(e: Expr) =
  when (e)  {
    is Num -> e.value
    is Sum -> eval(e.right) + eval(e.left)
   else -> throw IllegalArgumentException("unknown expression")
  }
Войти в полноэкранный режим Выйти из полноэкранного режима

Цикл While

страница 35

Циклы while и do while в Kotlin такие же, как и соответствующие им циклы в Java.

while (condition) {
  //while block
}

do {
  //do-while block
} while(condition)
Войти в полноэкранный режим Выход из полноэкранного режима

Циклы и диапазоны For

страницы 36, 37

Цикл for в Java: Мы инициализируем переменную, обновляем ее значение на каждом шаге цикла, выходим из цикла, когда значение достигает определенной границы.
Kotlin ввел диапазоны, чтобы заменить распространенный случай использования цикла.
Диапазон: Диапазон — это интервал между двумя значениями, обычно числами: начальным и конечным. Мы записываем его с помощью оператора ….
ПРИМЕЧАНИЕ: диапазоны в Kotlin являются замкнутыми или включающими (второе значение всегда является частью диапазона).

val oneToTen = 1..10
for (i in oneToTen) {
  println(i)
}

for (i in 1..10) {
  println(i)
}
Вход в полноэкранный режим Выйти из полноэкранного режима

В диапазонах мы можем использовать функции downTo, step и until.
downTo: выполняет итерацию в обратном направлении в цикле
step: позволяет пропустить некоторые числа
until: позволяет определить полузакрытый диапазон (последнее значение не входит в диапазон).

for (i in 100 downTo 1 step 2) {
  println(i) //prints 100, 98, 96 ... 4, 2
}

for (i in 1 until 100) {
  println(i) //prints 1, 2, 3, ..., 98, 99
}
Вход в полноэкранный режим Выход из полноэкранного режима

Итерация над коллекциями

страницы 37, 38

Наиболее распространенным случаем использования цикла for in является итерация по коллекции. Давайте посмотрим, как это можно сделать в Kotlin.

Сначала мы рассмотрим, как использовать цикл for для итерации по карте:

val binaryReps = TreeMap<Char, String>()
//here we will create a range of characters
for (c in 'A'..'F') {
  val binary = Integer.toBinary(c.toInt())
  //put each binary character in the map
  binaryReps[c] = binary
}

//iterating through the map
for ((letter, binary) in binaryReps) {
  println("$letter = $binary")
}
Войти в полноэкранный режим Выйти из полноэкранного режима

В приведенном выше коде мы видим, что функция range работает не только для чисел, но и отлично работает для символов.
Также видно, что мы распаковываем элементы коллекции для итерации по ним. В случае с картами мы должны распаковать ее в две переменные: одну для ключа, другую для значения.

ПРИМЕЧАНИЕ: Как мы видим, мы используем сокращенную версию доступа к значениям карты и их обновления. Мы используем map[key] = value вместо использования функций get и put:

Мы также можем выполнять итерации по коллекции и отслеживать индекс текущего элемента:

val list = arrayListOf("10", "11", "1001")
//here we iterate through each element in the list
for (element in list) {
    println("$element")
}

//here we iterate through each element in the list and keep the index of current element
for ((index, element) in list.withIndex()) {
    println("$index: $element")
}
Вход в полноэкранный режим Выйти из полноэкранного режима

Оператор in

страницы 38, 39

Мы можем использовать оператор in, чтобы проверить, находится ли значение в диапазоне, или его противоположность, !in, чтобы проверить, НЕ находится ли значение в диапазоне:

fun isLetter(c: Char) = c in 'a'..'z' || c in 'A'..'Z'

fun isNotDigit(c: Char) = c !in '0'..'9'
Вход в полноэкранный режим Выйти из полноэкранного режима

Операторы in и !in также работают в выражениях when:

fun recognize(c: Char) = when (c) {
    in '0'..'9' -> "It's a digit!"
    in 'a'..'z' , in 'A'..'Z' -> "It's a letter!"
    else -> "I don't know!"
}
Войти в полноэкранный режим Выйти из полноэкранного режима

ПРИМЕЧАНИЕ: Диапазоны также не ограничиваются символами. Если у вас есть любой класс, который поддерживает сравнение экземпляров (реализуя интерфейс java.lang.Comparable), вы можете создавать диапазоны объектов этого типа. Но имейте в виду, что вы не сможете перечислить все объекты в диапазоне.

Исключения

страницы 39, 40

Обработка исключений Kotlin похожа на Java. Функция может завершиться обычным образом или выбросить исключение. Либо вызывающая функция перехватывает и обрабатывает исключение, либо исключение распространяется и перебрасывается дальше по стеку.
В отличие от Java, в Kotlin конструкция throw является выражением и может использоваться как часть других выражений:

val percentage =
    if (number in 0..100)
        number
    else
        throw java.lang.IllegalArgumentException("Illegal percentage value")
Войти в полноэкранный режим выйти из полноэкранного режима

try, catch и finally

страницы 40, 41

Как и в Java, мы можем использовать try с блоками catch и finally.

fun readNumber(reader: BufferedReader): Int? {
    try {
        val line = reader.readLine()
        return Integer.parseInt(line)
    } catch (e: java.lang.NumberFormatException) {
        return null
    } finally {
        reader.close()
    }
}
Вход в полноэкранный режим Выход из полноэкранного режима

Самая большая разница в обработке исключений между Java и Kotlin заключается в том, что в коде НЕ присутствует предложение throws, поскольку Kotlin не делает различий между проверенными и непроверенными исключениями.

try как выражение

страницы 41, 42

Ключевое слово try в Kotlin, как и if и when, представляет собой выражение, и вы можете присвоить его переменной. В отличие от if, тело оператора try нужно заключать в фигурные скобки.

fun readNumber(reader: BufferedReader) {
    val number = try {
        val line = reader.readLine()
        Integer.parseInt(line)
    } catch (e: java.lang.NumberFormatException) {
        null
    }
}
Вход в полноэкранный режим Выход из полноэкранного режима

Если выполнение блока try проходит нормально, то последнее выражение в блоке является результатом. Если поймано исключение, то результатом будет последнее выражение в соответствующем блоке catch.

На этом мы заканчиваем часть 1, которая была кратким изложением главы 2. В главе 2 были и другие части, которые я не стал рассматривать здесь, потому что посчитал их не столь важными, и они сделали бы статью слишком длинной. В любом случае, если вы нашли эту статью полезной, пожалуйста, поставьте лайк и поделитесь ею с другими разработчиками. Если у вас есть вопросы или предложения, пожалуйста, оставляйте комментарии. Спасибо за ваше время.

Вот ссылка на вторую часть этой серии.

Оцените статью
devanswers.ru
Добавить комментарий