Kotlin изначально проектировался как безопасная и лаконичная альтернатива Java. Одной из его сильных сторон стал развитый механизм вывода типов (type inference), который позволяет писать менее многословный код без потери типизации. Type inference был вдохновлён функциональными языками (например, Scala и Haskell), а также современными трендами проектирования статически типизированных языков.
В Java и других статических языках требуется явно указывать типы, что ведёт к избыточности кода. Однако отсутствие явных типов может затруднить понимание кода и привести к неочевидным ошибкам, если вывод типа не сработает корректно.
В Kotlin компилятор часто может самостоятельно определить тип переменной или выражения исходя из контекста. Это работает для переменных, возвращаемых значений функций и внутри выражений с лямбдами. Однако есть ситуации, когда компилятор требует явно указать тип — например, при объявлении функции без возвращаемого значения внутри класса ('fun doSomething()') или когда выражения неоднозначны.
Пример кода:
val a = 42 // Int val s = "hello" // String fun sum(x: Int, y: Int) = x + y // возвращаемый тип Int выводится автоматически val list = listOf(1, 2, 3) // List<Int> // Явное указание типа необходимо, если значение не может быть выведено val emptyList: List<String> = emptyList() // иначе будет List<Nothing>
Ключевые особенности:
Почему нельзя всегда опускать тип после двоеточия, например, для свойств класса?
Для свойств, инициализируемых не в месте объявления (например, через геттер или в init-блоке), компилятор не может вывести тип автоматически, поскольку не видит инициализатора.
class User { val fullName: String // Обязательно указать тип, иначе ошибка get() = "name" }
Какой тип будет у переменной, если использовать emptyList() без явного типа?
Будет выведен тип List<Nothing>, что делает результат практически бесполезным.
val list = emptyList() // List<Nothing>
Когда вывод типа не работает с параметрами функций?
В сигнатуре функции всегда требуется явно указывать типы параметров, иначе компилятор выдаст ошибку.
// Ошибка: // fun foo(x) = x * 2 // Правильно: fun foo(x: Int) = x * 2
Разработчик использует emptyList() для возврата значения из функции API, не указывая тип явно. В результате получен тип List<Nothing>, что вызывает проблемы при работе с этим API.
Плюсы:
Разработчик всегда явно указывает тип при работе с пустыми коллекциями и там, где это улучшает читаемость, а в остальных случаях полагается на вывод типа компилятора.
Плюсы: