Переменные и базовые классы данных в Kotlin

Переменные в Kotlin объявляются с помощью ключевых слов var или val, за которыми идет имя переменной, через двоеточие указывается ее тип – класс данных.

Синтаксис объявления переменных в Kotlin

Переменной, объявленной через val, значение в программе можно присвоить только один раз. В дальнейшем значение можно только читать, val-переменные являются неизменяемыми.

fun main() {
    var a: Int 
    a = 5
    println(a)
}

Приведенный выше код правильный, но IntelliJ будет подсказывать, что его можно улучшить. Поскольку в программе мы не меняем значение переменной, ее следовало бы объявить через val. Однако поступим иначе, изменим ее:

fun main() {
    var a: Int
    a = 5
    println(a)
    a = a + 10
    println(a)
}

Тут тоже не все гладко. IntelliJ советует совместить объявление переменной с присвоением ей значения. Действительно, это уместно в данном случае. Ведь мы, например, не запрашиваем значение с ввода после выполнения каких-либо предварительных действий.

fun main() {
    var a: Int = 5
    println(a)
    a = a + 10
    println(a)
}

Теперь IntelliJ советует избавиться от Int, так как по значению и так понятен тип переменной. То есть Kotlin способен вывести тип переменной из присвоенных ей данных. Это называется автоматическим определением типов. Поэтому приводим код к такому виду:

fun main() {
    var a = 5
    println(a)
    a = a + 10
    println(a)
}

Это не значит, что переменная стала без типа. Kotlin – статически типизированный язык. Просто тип переменной был выведен, исходя из ее значения. Чтобы увидеть тип переменной в IntelliJ, надо установить на нее курсор и нажать Ctrl + Shift + P.

Осталось последнее замечание. IntelliJ считает, что добавление значения к переменной лучше записывать в сокращенной форме:

fun main() {
    var a = 5
    println(a)
    a += 10
    println(a)
}

В качестве вещественного типа по умолчанию используется класс Double:

fun main() {
    val a = 3.4
    println(a)
}

В данном случае тип переменной автоматически определится как Double. В Kotlin есть и другие числовые типы помимо Double и Int – это Float, Long, Short, Byte.

Булевый тип:

fun main() {
    val a = true
    val b = false
    var c: Boolean
    c = a > b
    println(c) // true
    c = a <= b
    println(c) // false
}

В Kotlin есть как строковый, так и символьный типы:

fun main() {
    val s = "Hello"
    val s2: String
    val c = 'W'
    val c2: Char
    s2 = s + c
    c2 = s[0]
    println(s2) // HelloW
    println(c2) // H
}

В Kotlin есть классы коллекций – списки (List), словари (Map), множества (Set).

Константы

Переменные, объявленные через val, – не константы. Ведь им не обязательно сразу присваивать значение. Это происходит в процессе выполнения программы. Например:

fun main() {
    val a = readLine()
    println(a)
}

Здесь более очевидно, что до компиляции значение переменной a неизвестно. Присвоение значения происходит в процессе выполнения программы, и значением может быть любая строка.

С другой стороны, в Kotlin есть, так сказать, настоящие константы – константы времени компиляции. Их значение известно заранее, "вшивается" в программу в момент ее компиляции.

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

const val N: Int = 10
 
fun main() {
    println(N)
}

Модификатор const не получится использовать внутри функции. Это не значит, что все переменные за пределами функции должны быть константами. Там могут быть и обычные переменные. Очевидно, сочетания const var быть не может.

Константы могут принадлежать только числовым типам, строковому, символьному, а также Boolean.

Nullable-типы

В Kotlin обычным типам нельзя присвоить литерал null – значение, говорящее, что переменная не связана с обычным объектом. Подобное ограничение в Kotlin призвано обеспечить nullable-безопасность, то есть таким образом избегается ряд ошибок при выполнении программы. Например, определенная функция не предполагает, что ей передадут null, и не умеет обрабатывать это значение, но получает null. В этом случае в процессе выполнения программы будет выброшено исключение.

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

С другой стороны, в Kotlin у каждого типа без поддержки null имеется аналогичный ему с поддержкой null-значения. При объявлении типа, второй отличается от первого знаком вопроса в конце. Например, есть два строковых типа: String – без поддержки null и String? с поддержкой null.

Nullable-типы в Kotlin

На скрине показано, что попытка присвоить null переменным a и c приводит к ошибке. В то же время, для nullable-переменных это позволительно, хотя не обязательно. Их значениями также могут быть числа и строки, как у обычных переменных.

При этом в функцию, параметр которой не поддерживает null, передать nullable-аргумент нельзя. Компилятор Котлина не даст скомпилировать такую программу.

Передача nullable-аргумента в функцию с не nullable-параметром приводит к ошибке компиляции

Встроенная функция readLine() возвращает строку, прочитанную с ввода. Если ввод происходит из файла, а не с клавиатуры, то readLine() может вернуть null, поэтому возвращаемый из нее тип String?. Переменную типа String? мы не можем передать в нашу функцию exPrint(), даже если значение этой переменной не будет null. Котлин – строго типизированный язык, поэтому String? и String для него – разные типы.

Выражение "$s!" – это строковый шаблон. После знака доллара указывается переменная, значение которой будет подставляться вместо нее.

Для обработки nullable-переменных в Kotlin предусмотрено несколько особых операторов, которые будут рассмотрены в следующем уроке.