Урок 3 из 8 · 25 мин · обновлено

Основы Kotlin: переменные, функции и защита от null

Главное из Kotlin для первых экранов: val и var, типы, строковые шаблоны, if и when, функции, списки и классы данных, а также как Kotlin защищает от ошибок с null.

val и var

Kotlin, как и Java, компилируемый язык со строгими типами, но писать на нём заметно короче. Переменные объявляют двумя словами:

Kotlin
val name = "Молоко"   // нельзя переназначить
var count = 2         // можно менять
count = 3
  • val — значение, которое после присваивания не меняется. По умолчанию пишут именно его;
  • var — переменная, когда значение действительно должно меняться.

Тип Kotlin определяет сам, но его можно указать явно через двоеточие:

Kotlin
val price: Int = 89
val weight: Double = 1.5
val inStock: Boolean = true

Основные типы: Int — целые числа, Double — дробные, String — строки, Boolean — true или false.

Строковые шаблоны подставляют значения прямо в текст:

Kotlin
println("$name: $count шт., итого ${price * count} ₽")
Результат
Молоко: 3 шт., итого 267 ₽

Знак $ подставляет переменную, а для выражения добавляют фигурные скобки.

if, when и функции

В Kotlin if и when возвращают значение, поэтому их можно сразу присвоить:

Kotlin
val delivery = if (total >= 1500) 0 else 199

val label = when (count) {
    0 -> "нет в списке"
    1 -> "одна штука"
    else -> "$count шт."
}

when — более мощный аналог switch: проверяет значение по вариантам сверху вниз, else срабатывает, если ничего не подошло.

Функции объявляют через fun, тип результата пишут после двоеточия:

Kotlin
fun total(price: Int, count: Int): Int {
    return price * count
}

fun discount(price: Int, percent: Int = 10): Int = price * (100 - percent) / 100

println(total(89, 3))
println(discount(1000))
println(discount(1000, percent = 25))
Результат
267
900
750

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

Списки и классы данных

Kotlin
data class Product(val name: String, val count: Int)

val products = listOf(
    Product("Молоко", 2),
    Product("Хлеб", 1),
    Product("Яблоки", 6),
)

for (p in products) {
    println("${p.name} — ${p.count}")
}

val many = products.filter { it.count > 1 }
println(many.map { it.name })
Результат
Молоко — 2
Хлеб — 1
Яблоки — 6
[Молоко, Яблоки]
  • data class — класс для хранения данных. Kotlin сам добавит ему сравнение, копирование и понятный вывод;
  • listOf создаёт неизменяемый список, mutableListOf — изменяемый, в который можно добавлять;
  • filter и map принимают лямбду — кусок кода в фигурных скобках. it — текущий элемент списка.

Защита от null

Самая частая ошибка в старых Android-приложениях — NullPointerException: программа обращается к значению, которого нет, и падает. Kotlin защищает от неё ещё при компиляции.

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

Kotlin
val name: String = null      // ошибка компиляции
val comment: String? = null  // можно: комментария может не быть

С таким значением Kotlin не даст работать напрямую. Есть безопасные операторы:

Kotlin
println(comment?.length)        // null: метод не вызван
println(comment?.length ?: 0)   // 0: запасное значение
Результат
null
0
  • ?. вызывает метод, только если значение не null, иначе возвращает null;
  • ?: (его называют «оператор Элвис») подставляет значение на случай null.

Есть ещё !! — «я уверен, что здесь не null». Если ошибётесь, приложение упадёт. Новичкам лучше его избегать.

Попробуйте сами. Код на Kotlin можно запускать в браузере в официальной песочнице play.kotlinlang.org. Напишите функцию describe(p: Product, note: String?): String, которая возвращает «Хлеб — 1 шт.» и добавляет примечание в скобках, если оно есть: «Молоко — 2 шт. (без лактозы)».

Решение
Kotlin
data class Product(val name: String, val count: Int)

fun describe(p: Product, note: String?): String {
    val base = "${p.name} — ${p.count} шт."
    return if (note != null) "$base ($note)" else base
}

fun main() {
    println(describe(Product("Хлеб", 1), null))
    println(describe(Product("Молоко", 2), "без лактозы"))
}
Результат
Хлеб — 1 шт.
Молоко — 2 шт. (без лактозы)

После проверки note != null Kotlin сам понимает, что внутри ветки значение точно есть, и разрешает использовать его как обычную строку. Это называется умным приведением типа.

Мини-тест: проверьте себя

Закрепите урок: несколько вопросов, разбор ответов — в конце.

  1. Чем val отличается от var?

  2. Что выведет println("Товаров: ${items.size}"), если в списке items три элемента?

  3. val name: String = null — что скажет Kotlin?

  4. Что вернёт выражение comment?.length ?: 0, если comment равен null?