Added kotlin guide. (WIP) (15.10.26)

This commit is contained in:
2026-10-15 00:24:21 +03:00
parent dc82207723
commit da3ad9dd1a
13 changed files with 2120 additions and 0 deletions
@@ -0,0 +1,154 @@
---
title: Переменные
description: val и var, вывод типов, const val, lateinit и области видимости
tags:
- kotlin
- syntax
---
Два ключевых слова:
- `val` - **value**, значение присваивается один раз (read-only)
- `var` - **variable**, значение можно менять
```kotlin
val name = "Kotlin"
var count = 0
count++ // ok
// name = "Java" // ошибка компиляции: val cannot be reassigned
```
Правило стиля: по умолчанию `val`, `var` - только когда переменная действительно меняется. Это не формальность: с `val` работают [умные приведения](../Null-безопасность/Умные%20приведения.md), а код становится предсказуемее.
## Объявление
```kotlin
val a: Int = 1 // тип указан явно
val b = 1 // тип выведен: Int
val c: Int // без инициализации — тип обязателен
c = 2 // присвоить можно позже, но только один раз
```
Тип ставится **после** имени через двоеточие.
## Вывод типов
Компилятор выводит тип из значения:
```kotlin
val n = 42 // Int
val l = 42L // Long
val s = "text" // String
val list = listOf(1, 2) // List<Int>
val mixed = listOf(1, "a") // List<Any>
```
Вывод работает только там, где есть что выводить. У свойств класса и возвращаемых значений публичных функций тип лучше писать явно - это часть контракта.
## val - не значит immutable
`val` запрещает переприсваивание ссылки, но не меняет сам объект:
```kotlin
val list = mutableListOf(1, 2)
list.add(3) // ok — меняем объект
println(list) // [1, 2, 3]
// list = mutableListOf() // ошибка — меняем ссылку
```
Неизменяемость данных - это выбор типа (`List` вместо `MutableList`), а не `val`. Подробнее: [Изменяемость коллекций](../../Коллекции/Изменяемость.md)
## val с геттером
`val` может вычисляться при каждом обращении:
```kotlin
val now: Long
get() = System.currentTimeMillis()
```
Это уже свойство, а не поле. Подробнее: [Свойства](../../ООП/Свойства.md)
## Константы - const val
```kotlin
const val MAX = 100 // верхний уровень файла
const val GREETING = "Hello"
object Config {
const val TIMEOUT = 30 // или внутри object
}
```
Требования к `const val`:
- только `String` или примитивный тип
- значение известно на этапе компиляции
- только на верхнем уровне, в `object` или в `companion object`
- нельзя у локальной переменной и нельзя с кастомным геттером
Значение подставляется в место использования на этапе компиляции (инлайнится), обращения к полю в рантайме нет.
```kotlin
val runtime = computeLimit() // вычисляется при запуске
const val compiled = 100 // подставлено компилятором
```
## lateinit
Для `var`, который точно будет проинициализирован, но не в момент объявления (DI, настройка в тестах):
```kotlin
class Service {
lateinit var client: HttpClient
fun setup() {
client = HttpClient()
}
fun use() {
if (::client.isInitialized) client.send()
}
}
```
Ограничения `lateinit`:
- только `var`
- тип не nullable
- не примитивный тип (`Int`, `Double`, … запрещены)
- нельзя у локальных переменных
- нельзя с кастомным геттером/сеттером
Обращение до инициализации - `UninitializedPropertyAccessException`.
Альтернатива для ленивых вычислений - `by lazy`, он работает с `val`:
```kotlin
val config: Config by lazy { loadConfig() } // вычислится при первом обращении
```
Подробнее: [Свойства](../../ООП/Свойства.md)
## Области видимости
```kotlin
const val TOP = 1 // весь файл и, если public, другие файлы
fun demo() {
val local = 2 // только тело функции
if (local > 1) {
val inner = 3 // только тело if
}
// println(inner) // ошибка: не видна
}
```
Вложенная переменная может скрыть внешнюю - компилятор выдаст предупреждение `name shadowed`.
> [!info] Отличия от Java
> | Java | Kotlin |
> |---|---|
> | `final int x = 1;` | `val x = 1` |
> | `int x = 1;` | `var x = 1` |
> | `var x = 1;` (только локально, с Java 10) | вывод типов везде, включая свойства класса |
> | `static final int MAX = 100;` | `const val MAX = 100` |
> | мутабельность по умолчанию | иммутабельность предпочтительна |
> | `int x;` - значение по умолчанию 0 | неинициализированную переменную читать нельзя |
>
> Важное следствие: у свойств класса в Kotlin **нет значений по умолчанию**. В Java поле `int count;` автоматически 0, в Kotlin `var count: Int` без инициализатора не скомпилируется.
>
> Сравнить: [Переменные в Java](../../../Программирование%20на%20Java/Синтаксис%20и%20типы/Базовый%20синтаксис/Переменные.md)
> [!warning] Подводные камни
> **`val` не делает объект неизменяемым** - см. пример с `MutableList` выше. Частая ошибка на собеседованиях.
>
> **`lateinit` прячет ошибку до рантайма.** Если забыли вызвать `setup()`, падение случится в момент использования, далеко от причины. Там, где можно, лучше конструктор или `by lazy`.
>
> **Вывод типов может дать не тот тип, что ожидали:**
> ```kotlin
> val x = 1 / 2 // 0, а не 0.5 — оба операнда Int
> val y = 1.0 / 2 // 0.5
> ```
>
> **`const val` зашивается в вызывающий код.** Если константа лежит в библиотеке и её значение изменилось, зависимый модуль нужно перекомпилировать - иначе он продолжит использовать старое значение.
Дополнительно: [Свойства](../../ООП/Свойства.md)
Далее: [Операторы и выражения](Операторы%20и%20выражения.md)