--- title: Any, Unit, Nothing description: Корень иерархии Any, тип-единица Unit и нижний тип Nothing tags: - kotlin - types --- Три особых типа образуют каркас системы типов Kotlin. | Тип | Сколько значений | Роль | |---|---|---| | `Any` | все объекты | супертип всего | | `Unit` | одно (`Unit`) | «результата нет» | | `Nothing` | ни одного | «возврата не будет» | ```mermaid graph TD A["Any?"] --> B["Any"] A --> N1["Nothing?"] B --> C["String"] B --> D["Int"] B --> E["Unit"] C --> N["Nothing"] D --> N E --> N N1 --> N ``` `Any` сверху, `Nothing` снизу: он подтип любого типа. ## Any Супертип всех non-null типов. Объявляет три метода: ```kotlin fun main() { val x: Any = 42 println(x.toString()) // 42 println(x.hashCode()) // 42 println(x == 42) // true — вызов equals } ``` Больше у `Any` ничего нет: ни `wait`, ни `notify`, ни `getClass`. ```kotlin fun main() { val items: List = listOf(1, "два", 3.0, true) println(items.joinToString { it::class.simpleName ?: "?" }) // Int, String, Double, Boolean } ``` `Any` не принимает `null` - для этого есть `Any?`, самый широкий тип в языке: ```kotlin fun main() { // val a: Any = null // ошибка компиляции val b: Any? = null // ok println(b) // null } ``` Расширения `Any?` можно звать на `null`: ```kotlin fun main() { val x: String? = null println(x.toString()) // "null" — расширение Any?.toString() println(x.hashCode()) // 0 } ``` Это не методы `Any`, а функции-расширения stdlib, объявленные для `Any?`. ## Unit Тип функций, которые ничего не возвращают. Это **настоящий объект**, а не отсутствие значения: ```kotlin fun log(msg: String): Unit = println(msg) fun main() { val result = log("привет") println(result) // kotlin.Unit println(result == Unit) // true } ``` `Unit` - `object`, у него ровно одно значение. Тип результата `Unit` можно опускать, и обычно так и делают: ```kotlin fun log(msg: String) = println(msg) // тип выведен: Unit fun log2(msg: String) { println(msg) } // то же самое ``` Поскольку `Unit` - полноценный тип, он подставляется в дженерики, где `void` в Java не работает: ```kotlin fun main() { val actions: List<() -> Unit> = listOf({ print("a") }, { print("b") }) actions.forEach { it() } // ab println() val map = mutableMapOf() map["ключ"] = Unit // Unit как значение — законно println(map) // {ключ=kotlin.Unit} } ``` В лямбде с типом результата `Unit` последнее выражение игнорируется - возвращать ничего не нужно: ```kotlin fun run2(block: () -> Unit) = block() fun main() { run2 { 42 } // 42 отбрасывается, лямбда подходит под () -> Unit println("ok") } ``` ## Nothing Тип без значений. Функция с результатом `Nothing` **не может завершиться нормально** - только исключением или бесконечным циклом: ```kotlin fun fail(message: String): Nothing = throw IllegalStateException(message) fun main() { val x: String = if (true) "ok" else fail("нет значения") println(x) // ok } ``` Зачем это нужно: `Nothing` - подтип любого типа, поэтому выражение с типом `Nothing` подходит в любую позицию. Компилятор знает, что дальше кода не будет. ```kotlin fun fail(msg: String): Nothing = throw IllegalArgumentException(msg) fun parse(s: String): Int = s.toIntOrNull() ?: fail("не число: $s") fun main() { println(parse("42")) // 42 // parse("abc") // IllegalArgumentException: не число: abc } ``` Если бы `fail` возвращала `Unit`, этот код не скомпилировался бы: `Int` и `Unit` несовместимы. `throw` сам по себе - выражение типа `Nothing`: ```kotlin fun main() { val x: Int = try { "abc".toInt() } catch (e: NumberFormatException) { throw IllegalStateException("плохие данные", e) // Nothing подходит } println(x) } ``` ### Анализ недостижимого кода ```kotlin fun fail(): Nothing = throw RuntimeException() fun main() { val x = 1 if (x > 0) { println("да") } else { fail() // println("недостижимо") // предупреждение компилятора } } ``` Компилятор использует `Nothing`, чтобы доказать: ветка не продолжается. Поэтому `TODO()` работает где угодно: ```kotlin fun calculate(x: Int): String = TODO("ещё не реализовано") fun main() { // calculate(1) // NotImplementedError println("компилируется") } ``` `TODO()` объявлена как `fun TODO(): Nothing`, поэтому подходит под любой тип результата. ### Nothing? и null ```kotlin fun main() { val x = null println(x) // null // тип x — Nothing?, единственное значение которого null val list = listOf(null, null) // List println(list.size) // 2 } ``` Пустые коллекции тоже используют `Nothing`: ```kotlin fun main() { val empty: List = emptyList() val strings: List = empty // подходит — List ковариантен println(strings.size) // 0 } ``` Один объект `EmptyList` подходит под `List` для любого `T` именно потому, что `List` - подтип всех `List`. > [!info] Отличия от Java > | Java | Kotlin | > |---|---| > | `Object` | `Any` (но без `wait`/`notify`/`getClass`) | > | `void` | `Unit` | > | `Void` | `Unit` (или `Nothing?`) | > | нет аналога | `Nothing` | > | `Object o = null;` | `Any? = null`, `Any` не принимает null | > > **`Any` беднее `Object`.** Методы `wait()`, `notify()`, `getClass()` в `Any` не объявлены: первые два относятся к устаревшей модели синхронизации, третий заменён на `::class`. На JVM они доступны после приведения к `Object`, но из Kotlin их намеренно не видно. > > **`Unit` против `void`** - не косметика. `void` не тип, поэтому в Java нельзя написать `List` или `Function`; приходится использовать `Void` (у которого единственное значение - `null`) или `Consumer`. В Kotlin `() -> Unit` - обычный тип. > > **`Nothing` в Java нет вообще.** Из-за этого Java-компилятор не знает, что метод никогда не вернёт управление: > ```java > int parse(String s) { > if (s == null) fail("null"); // fail объявлен void > return Integer.parseInt(s); // компилятор требует этот return > } > ``` > В Kotlin `fail(): Nothing` сообщает компилятору, что ветка обрывается, и анализ потока это учитывает. > > Сравнить: [Класс Object в Java](../../../Программирование%20на%20Java/Синтаксис%20и%20типы/Базовые%20типы%20ООП/Класс%20Object.md) > [!warning] Подводные камни > **`Any` - не то же самое, что `Any?`.** Функция, принимающая `Any`, не примет `null`: > ```kotlin > fun show(x: Any) = x.toString() > > fun main() { > val n: String? = null > // println(show(n)) // ошибка компиляции > println(show(n ?: "пусто")) // пусто > } > ``` > > **Лямбда с результатом `Unit` молча глотает значение:** > ```kotlin > fun main() { > val items = mutableListOf() > listOf(1, 2).forEach { items.add(it) } // add возвращает Boolean, > println(items) // но тип () -> Unit его отбрасывает > } > ``` > Это обычно удобно, но если по ошибке вместо `map` написать `forEach`, результат исчезнет без предупреждения. > > **`return` с типом `Unit` писать не нужно**, а `return Unit` - признак непонимания: > ```kotlin > fun f(): Unit { > return Unit // компилируется, но так не пишут > } > ``` > > **Функция с типом `Nothing` не может иметь тело-блок с `return`:** > ```kotlin > fun fail(): Nothing { > // return // ошибка: ничего нельзя вернуть > throw RuntimeException() // только выход через исключение > } > ``` > > **`val x = null` имеет тип `Nothing?`, а не `Any?`.** Присвоить такой переменной что-либо другое нельзя: > ```kotlin > fun main() { > var x = null > // x = "строка" // ошибка: ожидался Nothing? > val y: String? = null // так > println("$x $y") > } > ``` > > **`Nothing` в дженерике означает «значений не будет»:** > ```kotlin > fun main() { > val producer: () -> Nothing = { throw RuntimeException("всегда падает") } > // producer() — вызов гарантированно бросит исключение > println("тип ${producer::class.simpleName} объявлен, но вызов невозможен") > } > ``` > Если такой тип получился случайно (например, `emptyList()` без указания параметра), в коллекцию ничего нельзя добавить. Дополнительно: [equals, hashCode и toString](equals,%20hashCode%20и%20toString.md) Далее: [Data class](Data%20class.md)