200 вопросов по Kotlin
200 вопросов по Kotlin
Основы Kotlin
Вопрос
Что такое Kotlin?
Ответ
Kotlin — это статически типизированный язык программирования, разработанный компанией JetBrains. Он совместим с Java, компилируется в байт-код JVM, а также поддерживает компиляцию в JavaScript и нативный код через Kotlin/Native. Kotlin сочетает объектно-ориентированные и функциональные парадигмы.
Вопрос
Как объявить неизменяемую переменную в Kotlin?
Ответ
Неизменяемую переменную объявляют с помощью ключевого слова val:
val name = "Kotlin"
После присваивания значение нельзя изменить.
Вопрос
Как объявить изменяемую переменную в Kotlin?
Ответ
Изменяемую переменную объявляют с помощью ключевого слова var:
var counter = 0
counter = 1 // допустимо
Вопрос
Обязательно ли указывать тип переменной при объявлении?
Ответ
Нет, не обязательно. Kotlin поддерживает вывод типов. Тип выводится из значения справа от знака присваивания:
val age = 25 // тип Int выводится автоматически
Тип можно указать явно, если требуется:
val name: String = "Alex"
Вопрос
Какие примитивные типы данных есть в Kotlin?
Ответ
В Kotlin нет настоящих примитивов как в Java. Все типы являются объектами, но компилятор оптимизирует их до примитивов JVM там, где возможно. Основные типы:
Int,Long,Short,ByteFloat,DoubleBooleanChar
Вопрос
Как объявить константу на уровне пакета?
Ответ
Константу на уровне файла (пакета) объявляют с помощью const val:
const val PI = 3.14159
Ключевое слово const допустимо только для значений, известных во время компиляции, и только на верхнем уровне или в объектах.
Вопрос
Чем отличается val от const val?
Ответ
val — неизменяемая переменная, значение которой может быть вычислено во время выполнения.
const val — константа времени компиляции, доступна только на верхнем уровне или в объектах, и её значение должно быть известно на этапе компиляции.
Вопрос
Как работает вывод типов в Kotlin?
Ответ
Компилятор Kotlin автоматически определяет тип выражения на основе правой части присваивания или возвращаемого значения функции. Например:
val list = listOf(1, 2, 3) // тип List<Int>
fun add(a: Int, b: Int) = a + b // возвращаемый тип Int
Вопрос
Можно ли в Kotlin использовать null без специальных мер?
Ответ
Нет. По умолчанию типы в Kotlin не допускают значение null. Чтобы разрешить null, нужно явно указать nullable-тип, добавив ?:
var name: String? = null
Вопрос
Что такое nullable-типы в Kotlin?
Ответ
Nullable-типы — это типы, которые могут содержать значение null. Они обозначаются суффиксом ?:
val email: String? = getUserEmail() // может вернуть null
Работа с nullable-типами требует безопасного обращения: через операторы ?., ?:, проверки if или явное приведение.
Вопрос
Как безопасно вызвать метод у nullable-переменной?
Ответ
Используется оператор безопасного вызова ?.:
val length = name?.length // вернёт Int? или null, если name == null
Если объект не null, метод вызывается; иначе результат — null.
Вопрос
Что делает оператор Elvis (?:)?
Ответ
Оператор Elvis возвращает левое значение, если оно не null, иначе — правое:
val displayName = name ?: "Guest"
Эквивалентно:
val displayName = if (name != null) name else "Guest"
Вопрос
Что произойдёт при использовании оператора !!?
Ответ
Оператор !! принудительно преобразует nullable-тип в ненулевой. Если значение равно null, выбрасывается исключение NullPointerException:
val length = name!!.length // NPE, если name == null
Использование !! считается небезопасным и применяется редко.
Вопрос
Как проверить, что переменная не равна null, и использовать её внутри блока?
Ответ
Можно использовать обычную проверку if:
if (name != null) {
println(name.length) // компилятор знает, что name не null
}
Внутри блока name автоматически рассматривается как ненулевой тип благодаря smart cast.
Вопрос
Что такое smart cast в Kotlin?
Ответ
Smart cast — это механизм, при котором компилятор автоматически приводит тип переменной после проверки на null или is:
if (obj is String) {
println(obj.length) // obj уже String, не нужно явное приведение
}
Smart cast работает только для val или при гарантии неизменности переменной.
Вопрос
Как объявить функцию в Kotlin?
Ответ
Функцию объявляют с помощью ключевого слова fun:
fun greet(name: String): String {
return "Hello, $name!"
}
Если тело состоит из одного выражения, можно использовать сокращённую форму:
fun greet(name: String) = "Hello, $name!"
Вопрос
Можно ли опускать тип возвращаемого значения функции?
Ответ
Да, если функция возвращает Unit (аналог void в Java) или если тип выводится автоматически:
fun printMessage(msg: String) {
println(msg) // неявный Unit
}
fun square(x: Int) = x * x // тип Int выводится
Для функций с блочным телом и непустым возвратом тип указывать обязательно.
Вопрос
Что такое Unit в Kotlin?
Ответ
Unit — это тип, представляющий отсутствие значимого возвращаемого значения. Аналог void в Java. Функции, ничего не возвращающие явно, имеют тип Unit.
Вопрос
Что такое Nothing в Kotlin?
Ответ
Nothing — это тип, который не имеет значений. Он используется для обозначения функций, которые никогда не завершаются нормально (например, выбрасывают исключение или зацикливаются):
fun fail(message: String): Nothing {
throw IllegalStateException(message)
}
Nothing является подтипом всех типов.
Вопрос
Как передать параметры по умолчанию в функцию?
Ответ
Параметры по умолчанию задаются прямо в сигнатуре функции:
fun greet(name: String, title: String = "Mr.") = "Hello, $title $name!"
Вызов: greet("Alice") или greet("Bob", "Dr.").
Вопрос
Что такое именованные аргументы?
Ответ
Именованные аргументы позволяют передавать параметры функции по имени, а не по позиции:
greet(name = "Alice", title = "Ms.")
Это повышает читаемость, особенно при наличии параметров по умолчанию.
Вопрос
Как объявить функцию с переменным числом аргументов?
Ответ
Используется модификатор vararg:
fun printAll(vararg messages: String) {
messages.forEach { println(it) }
}
Вызов: printAll("A", "B", "C").
Вопрос
Можно ли передать массив как vararg?
Ответ
Да, с помощью оператора распаковки *:
val messages = arrayOf("X", "Y")
printAll(*messages)
Вопрос
Что такое однострочные (single-expression) функции?
Ответ
Это функции, тело которых состоит из одного выражения. Их можно записать без фигурных скобок и return:
fun add(a: Int, b: Int) = a + b
Тип возвращаемого значения выводится автоматически.
Вопрос
Как работают строки в Kotlin?
Ответ
Строки в Kotlin неизменяемы и поддерживают интерполяцию через $:
val name = "Kotlin"
println("Hello, $name!") // Hello, Kotlin!
Для выражений используется ${}:
println("Next year: ${2025 + 1}")
Вопрос
Какие виды строковых литералов существуют в Kotlin?
Ответ
Два вида:
-
Обычные строки — в двойных кавычках, поддерживают escape-последовательности:
val s = "Hello\nWorld" -
Сырые строки (raw strings) — в тройных кавычках, не обрабатывают escape-последовательности, удобны для многострочного текста:
val sql = """
SELECT *
FROM users
WHERE id = 1
""".trimIndent()
Вопрос
Что делает trimIndent()?
Ответ
trimIndent() удаляет общее начальное отступление из каждой строки сырых строк, выравнивая текст по левому краю:
val text = """
a
b
c
""".trimIndent()
// Результат: "a\nb\nc"
Вопрос
Как сравнить две строки на равенство?
Ответ
Оператор == в Kotlin сравнивает содержимое (структурное равенство), а не ссылки:
val a = "text"
val b = "text"
println(a == b) // true
Это эквивалентно a.equals(b) в Java.
Вопрос
Как создать массив в Kotlin?
Ответ
Массив создаётся с помощью функции arrayOf() или типизированных фабрик:
val arr = arrayOf(1, 2, 3)
val intArr = intArrayOf(1, 2, 3) // для примитивов
Тип массива — Array<T> для объектов, IntArray, DoubleArray и т.д. — для примитивов.
Вопрос
Чем Array<Int> отличается от IntArray?
Ответ
Array<Int> — это массив объектов Integer (упакованных).
IntArray — это массив примитивов int, более эффективный по памяти и производительности.
Kotlin предоставляет специализированные массивы для всех числовых примитивов.
Вопрос
Как получить длину массива?
Ответ
Через свойство size:
val arr = arrayOf(1, 2, 3)
println(arr.size) // 3
Вопрос
Как объявить список в Kotlin?
Ответ
С помощью функций listOf() (неизменяемый) или mutableListOf() (изменяемый):
val immutable = listOf("a", "b")
val mutable = mutableListOf("x", "y")
Вопрос
Каковы основные интерфейсы коллекций в Kotlin?
Ответ
Основные интерфейсы:
Collection<T>— базовый интерфейс для всех коллекций.List<T>— упорядоченная коллекция с доступом по индексу.Set<T>— коллекция без дубликатов.Map<K, V>— ассоциативный массив (ключ-значение).
Для каждого есть изменяемые (MutableList, MutableSet, MutableMap) и неизменяемые версии.
Вопрос
Можно ли изменить список, созданный через listOf()?
Ответ
Нет. Список, созданный через listOf(), является неизменяемым. Попытка вызвать add(), remove() и т.п. приведёт к исключению UnsupportedOperationException.
Вопрос
Как проверить, содержит ли коллекция элемент?
Ответ
Используется оператор in:
val list = listOf(1, 2, 3)
println(2 in list) // true
Эквивалентно list.contains(2).
Вопрос
Как перебрать элементы списка?
Ответ
Через цикл for:
for (item in list) {
println(item)
}
Можно использовать индексы:
for (i in list.indices) {
println("$i: ${list[i]}")
}
Вопрос
Как работает цикл for в Kotlin?
Ответ
Цикл for перебирает любой объект, реализующий Iterator. Он не использует условие, как в C-подобных языках:
for (i in 1..5) { } // от 1 до 5 включительно
for (i in 1 until 5) { } // от 1 до 4
for (i in 5 downTo 1) { } // от 5 до 1
for (i in 1..10 step 2) { } // с шагом 2
Вопрос
Что такое диапазоны (ranges) в Kotlin?
Ответ
Диапазоны — это упорядоченные последовательности значений, создаваемые операторами .., until, downTo. Примеры:
1..5 // 1, 2, 3, 4, 5
1 until 5 // 1, 2, 3, 4
5 downTo 1 // 5, 4, 3, 2, 1
Диапазоны реализуют интерфейс ClosedRange.
Вопрос
Как проверить, входит ли число в диапазон?
Ответ
С помощью оператора in:
val x = 3
println(x in 1..5) // true
Вопрос
Как работает оператор when?
Ответ
when заменяет switch из Java. Он сравнивает значение с несколькими ветками и выполняет первую подходящую:
when (x) {
1 -> println("One")
2, 3 -> println("Two or three")
in 4..10 -> println("Between four and ten")
else -> println("Other")
}
when может использоваться как выражение (возвращать значение).
Вопрос
Может ли when работать без аргумента?
Ответ
Да. В этом случае каждая ветка должна быть булевым выражением:
when {
x < 0 -> println("Negative")
x > 0 -> println("Positive")
else -> println("Zero")
}
Вопрос
Что такое sealed class?
Ответ
sealed class — это класс, который ограничивает иерархию наследования. Все подклассы должны быть объявлены в том же файле. Часто используется с when для полного покрытия вариантов:
sealed class Result
class Success(val data: String) : Result()
class Error(val message: String) : Result()
fun handle(result: Result) = when (result) {
is Success -> ...
is Error -> ...
}
Компилятор гарантирует, что все случаи учтены.
Вопрос
Как объявить перечисление (enum) в Kotlin?
Ответ
С помощью ключевого слова enum class:
enum class Color {
RED, GREEN, BLUE
}
Можно добавлять свойства и методы:
enum class Color(val rgb: Int) {
RED(0xFF0000),
GREEN(0x00FF00);
}
Вопрос
Как получить все значения enum?
Ответ
Через статический метод values():
val allColors = Color.values()
Вопрос
Что такое data class?
Ответ
data class — это класс, предназначенный для хранения данных. Компилятор автоматически генерирует:
equals()иhashCode()toString()copy()- компонентные функции (
component1(),component2(), …)
Пример:
data class User(val name: String, val age: Int)
Вопрос
Как работает метод copy() у data class?
Ответ
copy() создаёт новый экземпляр с теми же значениями, но позволяет изменить некоторые поля:
val user = User("Alice", 30)
val updated = user.copy(age = 31)
Вопрос
Можно ли наследовать data class?
Ответ
Нет. data class не может быть открыт для наследования (open). Он автоматически становится final.
Вопрос
Что такое делегирование свойств (Delegated Properties)?
Ответ
Это механизм, при котором логика получения и установки значения свойства выносится в отдельный объект-делегат. Часто используется для ленивой инициализации, наблюдаемых свойств и т.д.
Пример:
val lazyValue by lazy { computeExpensiveValue() }
Здесь lazy — стандартная делегирующая функция.
Вопрос
Как работает by lazy?
Ответ
by lazy инициализирует свойство при первом обращении к нему. Инициализация происходит потокобезопасно по умолчанию:
val database by lazy { createDatabaseConnection() }
Последующие обращения возвращают закэшированное значение.
Вопрос
Как создать наблюдаемое свойство?
Ответ
С помощью делегата Delegates.observable:
var name by Delegates.observable("default") { prop, old, new ->
println("$old -> $new")
}
Функция вызывается при каждом изменении значения.
Вопрос
Что такое companion object?
Ответ
companion object — это объект, связанный с классом, аналог статических членов в Java. Его методы и свойства вызываются через имя класса:
class MyClass {
companion object {
fun create() = MyClass()
}
}
val instance = MyClass.create()
Вопрос
Можно ли иметь несколько companion object в одном классе?
Ответ
Нет. В классе может быть только один companion object.
Вопрос
Как вызвать метод из companion object из Java?
Ответ
По умолчанию методы companion object доступны как статические через имя класса и внутренний объект Companion:
MyClass.Companion.create();
Чтобы сделать их действительно статическими, используют аннотацию @JvmStatic:
companion object {
@JvmStatic fun create() = MyClass()
}
Теперь в Java: MyClass.create();.
Вопрос
Что такое extension-функции?
Ответ
Extension-функции позволяют добавлять новые функции к существующим классам без наследования или изменения исходного кода:
fun String.isValidEmail(): Boolean = this.contains("@")
Вызов: "test@example.com".isValidEmail().
Вопрос
Могут ли extension-функции обращаться к приватным членам класса?
Ответ
Нет. Extension-функции не имеют доступа к приватным или защищённым членам расширяемого класса. Они работают только с публичным API.
Вопрос
Где реально располагаются extension-функции после компиляции?
Ответ
Extension-функции компилируются в статические методы в классе, соответствующем файлу, где они объявлены. При вызове передаётся экземпляр как первый параметр.
Вопрос
Что такое infix-функции?
Ответ
Infix-функции позволяют вызывать функцию без точек и скобок, помещая её между двумя операндами. Объявляются с модификатором infix:
infix fun Int.times(str: String) = str.repeat(this)
val result = 3 times "Ha" // "HaHaHa"
Должны быть member-функциями или extension-функциями с одним параметром.
Вопрос
Какие ограничения у infix-функций?
Ответ
- Должны принимать ровно один параметр.
- Не могут быть переопределены (
override). - Не могут быть объявлены как
vararg. - Не могут быть generic-функциями с неограниченными параметрами типа.
Вопрос
Что такое операторные функции (operator fun)?
Ответ
Это функции, которые переопределяют поведение операторов (например, +, -, [], invoke). Пример:
operator fun Point.plus(other: Point) = Point(x + other.x, y + other.y)
Теперь можно писать: p1 + p2.
Вопрос
Какие операторы можно переопределять?
Ответ
Можно переопределять:
- Арифметические:
plus,minus,times,div,rem - Унарные:
unaryPlus,unaryMinus,not - Сравнения:
compareTo,equals - Индексации:
get,set - Вызова:
invoke - Диапазонов:
rangeTo
Полный список фиксирован компилятором.
Вопрос
Как работает get и set как операторные функции?
Ответ
Они позволяют использовать синтаксис индексации:
operator fun MyList.get(index: Int) = items[index]
operator fun MyList.set(index: Int, value: String) { items[index] = value }
val item = list[0]
list[0] = "new"
Вопрос
Что такое apply, let, run, with, also?
Ответ
Это функции области видимости (scope functions), упрощающие работу с объектами:
apply: выполняет блок, возвращая сам объект. Используется для инициализации.let: выполняет блок, если объект неnull, и возвращает результат блока.run: выполняет блок и возвращает его результат. Может вызываться как extension или как обычная функция.with: принимает объект и выполняет блок над ним (не extension).also: похож наapply, но возвращает объект, а не результат блока; часто используется для побочных эффектов.
Вопрос
Когда использовать let вместо if (obj != null)?
Ответ
let удобен для цепочек вызовов и преобразований:
email?.let { sendEmail(it) }
Это короче и выразительнее, чем отдельный if.
Вопрос
Чем apply отличается от also?
Ответ
Обе возвращают исходный объект. Разница в приёме:
- В
applyблок получаетthisкак контекст. - В
alsoблок получает объект как параметр (it).
Пример:
val person = Person().apply {
name = "Alice"
age = 30
}
person.also { log("Created: $it") }
Вопрос
Как работает run как extension и как обычная функция?
Ответ
- Как extension:
obj.run { ... }—thisвнутри блока —obj. - Как обычная функция:
run { ... }— используется для группировки кода с возвратом значения.
Вопрос
Что такое inline-функции?
Ответ
inline-функции встраиваются в точку вызова на этапе компиляции, устраняя накладные расходы на вызов функции и аллокацию лямбд. Особенно полезны для функций высшего порядка:
inline fun measureTime(block: () -> Unit): Long {
val start = System.currentTimeMillis()
block()
return System.currentTimeMillis() - start
}
Вопрос
Какие недостатки у inline-функций?
Ответ
- Увеличивают размер байт-кода.
- Не позволяют сохранять ссылку на лямбду (например, присвоить переменной).
- Затрудняют отладку, так как код встраивается.
Вопрос
Что такое noinline и crossinline?
Ответ
noinline— предотвращает встраивание конкретного лямбда-параметра вinline-функции.crossinline— запрещает лямбде использовать non-local return (например,returnиз внешней функции), если она будет вызвана не напрямую.
Вопрос
Как обработать исключение в Kotlin?
Ответ
С помощью блоков try/catch/finally. try является выражением:
val result = try {
riskyOperation()
} catch (e: IOException) {
handleError(e)
} finally {
cleanup()
}
Вопрос
Поддерживает ли Kotlin checked exceptions?
Ответ
Нет. Kotlin не требует объявления или обработки checked exceptions, как это делает Java. Все исключения считаются unchecked.
Вопрос
Можно ли использовать try как выражение без catch?
Ответ
Нет. Если используется try как выражение, должен быть хотя бы один catch. Но можно использовать try как оператор без возврата значения.
Объектно-ориентированное программирование
Вопрос
Как объявить класс в Kotlin?
Ответ
Класс объявляется с помощью ключевого слова class:
class Person(val name: String, var age: Int)
Если тело класса пустое, фигурные скобки можно опустить.
Вопрос
Что такое первичный конструктор (primary constructor)?
Ответ
Первичный конструктор — это часть заголовка класса, сразу после имени. Он может содержать параметры, которые становятся свойствами класса при использовании val/var:
class User(val id: Int, var name: String)
Параметры без val/var доступны только в блоке инициализации (init) и конструкторах.
Вопрос
Можно ли иметь несколько конструкторов в классе?
Ответ
Да. Первичный конструктор может быть один. Дополнительные конструкторы (secondary constructors) объявляются внутри тела класса с помощью constructor и должны делегировать вызов первичному или другому вторичному конструктору:
class Person {
constructor(name: String) { /* ... */ }
constructor(name: String, age: Int) : this(name) { /* ... */ }
}
Вопрос
Что такое блок init?
Ответ
Блок init — это инициализационный блок, выполняемый при создании экземпляра класса. Он используется для проверки параметров, логики инициализации и т.д.:
class User(val name: String) {
init {
require(name.isNotBlank()) { "Name must not be blank" }
}
}
Все init-блоки выполняются в порядке их объявления.
Вопрос
Как наследовать класс в Kotlin?
Ответ
По умолчанию классы в Kotlin запечатаны (final). Чтобы разрешить наследование, нужно пометить класс как open:
open class Animal(val name: String)
class Dog(name: String) : Animal(name)
Конструктор базового класса вызывается сразу после двоеточия.
Вопрос
Как переопределить метод в Kotlin?
Ответ
Метод должен быть помечен как open в родительском классе. В дочернем классе используется ключевое слово override:
open class Shape {
open fun draw() = println("Drawing shape")
}
class Circle : Shape() {
override fun draw() = println("Drawing circle")
}
Вопрос
Можно ли переопределить свойство?
Ответ
Да. Свойство должно быть open в родительском классе:
open class Vehicle {
open val maxSpeed: Int = 100
}
class SportsCar : Vehicle() {
override val maxSpeed: Int = 300
}
Вопрос
Что такое абстрактный класс?
Ответ
Абстрактный класс объявляется с модификатором abstract. Он не может быть инстанцирован и может содержать как реализованные, так и абстрактные члены:
abstract class Figure {
abstract fun area(): Double
fun printArea() = println(area())
}
Подклассы обязаны реализовать все абстрактные методы.
Вопрос
Чем интерфейс отличается от абстрактного класса?
Ответ
Интерфейс (interface) не содержит состояния (за исключением const val), но может иметь реализацию методов по умолчанию. Абстрактный класс может хранить состояние и имеет конструктор.
Пример интерфейса:
interface Drawable {
fun draw()
fun resize(factor: Double) = println("Resizing by $factor") // default impl
}
Класс может реализовать несколько интерфейсов, но наследовать только один класс.
Вопрос
Как реализовать несколько интерфейсов?
Ответ
Указываются через запятую после двоеточия:
class Screen : Drawable, Clickable {
override fun draw() = println("Screen drawn")
override fun click() = println("Screen clicked")
}
Вопрос
Что происходит, если два интерфейса содержат метод с одинаковой сигнатурой?
Ответ
Компилятор требует явного разрешения конфликта с помощью супер-вызова:
interface A { fun foo() = println("A") }
interface B { fun foo() = println("B") }
class C : A, B {
override fun foo() {
A.super.foo() // или B.super.foo()
}
}
Вопрос
Какие модификаторы видимости есть в Kotlin?
Ответ
public— доступен везде (по умолчанию).private— только внутри файла (для верхнего уровня) или класса.protected— внутри класса и его подклассов.internal— внутри модуля (например, одного Gradle-модуля).
Вопрос
Что означает internal?
Ответ
internal делает член доступным всему модулю компиляции. Это полезно для скрытия API от внешних пользователей, но предоставления доступа внутри проекта.
Вопрос
Можно ли сделать конструктор приватным?
Ответ
Да. Конструктор можно скрыть с помощью private:
class Secret private constructor() {
companion object {
fun create() = Secret()
}
}
Это часто используется в паттерне Singleton или фабриках.
Вопрос
Что такое вложенные (nested) и внутренние (inner) классы?
Ответ
- Вложенный класс (
class Nested) — статический по смыслу, не имеет доступа к экземпляру внешнего класса. - Внутренний класс (
inner class Inner) — имеет доступ к экземпляру внешнего класса черезthis@Outer.
Пример:
class Outer {
private val x = 1
inner class Inner {
fun printX() = println(x) // доступ к x
}
}
Вопрос
Как вызвать метод родительского класса?
Ответ
С помощью super:
class Child : Parent() {
override fun greet() {
super.greet() // вызов родительской реализации
println("Hello from child")
}
}
Вопрос
Что такое делегирование в Kotlin?
Ответ
Kotlin поддерживает делегирование на уровне языка с помощью ключевого слова by. Это позволяет реализовать паттерн «делегирования вместо наследования»:
interface Printer {
fun print(message: String)
}
class ConsolePrinter : Printer {
override fun print(message: String) = println(message)
}
class LoggingPrinter(printer: Printer) : Printer by printer {
override fun print(message: String) {
println("Logging: $message")
printer.print(message)
}
}
Вопрос
Можно ли делегировать реализацию интерфейса нескольким объектам?
Ответ
Нет. Один интерфейс может быть делегирован только одному объекту. Однако можно комбинировать делегирование с ручной реализацией.
Вопрос
Что такое объект-выражение (object expression)?
Ответ
Объект-выражение создаёт анонимный объект, обычно для реализации интерфейса или абстрактного класса:
val listener = object : OnClickListener {
override fun onClick() = println("Clicked!")
}
Оно аналогично анонимному классу в Java.
Вопрос
Чем объект-выражение отличается от object declaration?
Ответ
- Объект-выражение создаёт новый экземпляр каждый раз.
- Объект-объявление (
object MyObject) — это singleton, создаётся один раз при первом использовании.
Вопрос
Как создать singleton в Kotlin?
Ответ
С помощью object:
object DatabaseManager {
fun connect() = println("Connected")
}
Вызов: DatabaseManager.connect().
Обобщения (Generics)
Вопрос
Как объявить обобщённый класс?
Ответ
Тип-параметр указывается в угловых скобках:
class Box<T>(value: T) {
var value = value
}
Использование: val box = Box<String>("Hello").
Вопрос
Что такое вариантность (variance) в Kotlin?
Ответ
Вариантность определяет, как типы с параметрами связаны между собой. Kotlin поддерживает:
- Ковариантность (
out T) — только чтение. - Контравариантность (
in T) — только запись. - Инвариантность (по умолчанию) — чтение и запись.
Вопрос
Что означает out в обобщениях?
Ответ
out T означает, что T используется только как возвращаемый тип (производится). Такой тип ковариантен:
interface Producer<out T> {
fun produce(): T
}
Теперь Producer<String> является подтипом Producer<Any>.
Вопрос
Что означает in в обобщениях?
Ответ
in T означает, что T используется только как параметр (потребляется). Такой тип контравариантен:
interface Consumer<in T> {
fun consume(item: T)
}
Теперь Consumer<Any> является подтипом Consumer<String>.
Вопрос
Почему MutableList не ковариантен?
Ответ
Потому что MutableList позволяет и читать, и изменять содержимое. Если бы он был ковариантен, можно было бы добавить неправильный тип:
val strings: MutableList<String> = mutableListOf()
val anys: MutableList<Any> = strings // запрещено!
anys.add(42) // нарушение типа!
Поэтому MutableList инвариантен.
Вопрос
Что такое звёздочный проектор (star projection)?
Ответ
Это способ сказать «я не знаю, какой тип здесь, и не хочу знать». Обозначается как *:
val list: List<*> = getList() // безопасно, только чтение
Полезно при работе с Java-кодом или когда тип не важен.
Вопрос
Как ограничить тип-параметр?
Ответ
С помощью where или указания верхней границы:
fun <T : Comparable<T>> sort(list: List<T>): List<T> = list.sorted()
// Или с несколькими ограничениями:
fun <T> cloneWhenValid(item: T) where T : Cloneable, T : Serializable { }
Функциональные возможности
Вопрос
Что такое функция высшего порядка?
Ответ
Функция высшего порядка — это функция, которая принимает другие функции в качестве параметров или возвращает функцию:
fun measure(block: () -> Unit): Long {
val start = System.currentTimeMillis()
block()
return System.currentTimeMillis() - start
}
Вопрос
Как объявить тип функции?
Ответ
Тип функции записывается как (аргументы) -> Возврат:
val add: (Int, Int) -> Int = { a, b -> a + b }
Вопрос
Что такое лямбда-выражение?
Ответ
Лямбда — это краткая запись анонимной функции:
val square = { x: Int -> x * x }
Параметры указываются до ->, тело — после.
Вопрос
Как обращаться к параметру лямбды по умолчанию?
Ответ
Параметры лямбды можно называть it, если она принимает ровно один аргумент:
list.filter { it > 0 }.map { it * 2 }
Вопрос
Что такое ссылка на функцию (function reference)?
Ответ
Ссылка на функцию создаётся с помощью оператора :::
fun isPositive(x: Int) = x > 0
val numbers = listOf(-1, 0, 1, 2)
val positives = numbers.filter(::isPositive)
Вопрос
Можно ли передавать методы экземпляра как ссылки?
Ответ
Да, с привязкой к экземпляру:
val str = "Hello"
val upper = str::toUpperCase
println(upper()) // "HELLO"
Вопрос
Что такое каррирование в Kotlin?
Ответ
Kotlin не поддерживает каррирование напрямую, но его можно реализовать вручную:
fun add(a: Int) = { b: Int -> a + b }
val add5 = add(5)
println(add5(3)) // 8
Вопрос
Как работает замыкание в Kotlin?
Ответ
Лямбда захватывает переменные из окружающей области видимости:
var counter = 0
val increment = { counter++ }
increment()
println(counter) // 1
Захваченные переменные должны быть var или val, но не изменяемыми примитивами в строгом смысле (в JVM всё объекты).
Вопрос
Что такое SAM-конверсия?
Ответ
SAM (Single Abstract Method) — это интерфейс с одним абстрактным методом. Kotlin автоматически преобразует лямбду в такой интерфейс:
interface Runnable {
fun run()
}
fun execute(r: Runnable) = r.run()
execute { println("Running!") } // SAM conversion
Работает только с Java-интерфейсами или Kotlin-интерфейсами, помеченными как fun interface.
Вопрос
Что такое fun interface?
Ответ
Это интерфейс с ровно одним абстрактным методом, оптимизированный для использования с лямбдами:
fun interface Predicate<T> {
fun test(value: T): Boolean
}
val isEven = Predicate<Int> { it % 2 == 0 }
Компилятор генерирует меньше байт-кода по сравнению с обычным интерфейсом.
Вопрос
Можно ли перегружать функции?
Ответ
Да. Kotlin поддерживает перегрузку функций с разными сигнатурами:
fun log(msg: String) = println(msg)
fun log(msg: String, level: String) = println("[$level] $msg")
Перегрузка по возвращаемому типу невозможна.
Вопрос
Как работает переопределение свойства с делегированием?
Ответ
Свойство может использовать делегат:
class User {
var name: String by Delegates.observable("Guest") { _, old, new ->
println("$old → $new")
}
}
Вопрос
Что такое lateinit?
Ответ
lateinit позволяет объявить не-null свойство без немедленной инициализации. Используется для свойств, которые будут инициализированы позже (например, во фреймворках):
lateinit var adapter: RecyclerView.Adapter<*>
Нельзя использовать с примитивами (Int, Boolean и т.д.) и nullable-типами.
Вопрос
Как проверить, инициализировано ли lateinit-свойство?
Ответ
С помощью ::property.isInitialized:
if (::adapter.isInitialized) {
// безопасно использовать
}
Вопрос
Что такое init в контексте свойств?
Ответ
init не применяется к свойствам напрямую. Возможно, имеется в виду блок инициализации класса (init {}) или делегат lazy, который инициализируется при первом доступе.
Вопрос
Как работает наследование с первичным конструктором?
Ответ
Параметры первичного конструктора базового класса передаются сразу после двоеточия:
open class Animal(val name: String)
class Cat(name: String, val color: String) : Animal(name)
Вопрос
Можно ли вызвать конструктор базового класса из вторичного?
Ответ
Да, через цепочку делегирования:
open class Base(val x: Int)
class Derived : Base {
constructor(a: Int) : super(a * 2)
}
Вопрос
Что такое companion object и зачем он нужен?
Ответ
companion object — это singleton, связанный с классом. Его члены вызываются через имя класса, как статические в Java:
class StringUtils {
companion object {
fun isEmpty(s: String) = s.isEmpty()
}
}
StringUtils.isEmpty("") // вызов
Вопрос
Можно ли наследовать companion object?
Ответ
Нет. companion object не наследуется. Однако можно реализовать интерфейс внутри него:
interface Factory<T> {
fun create(): T
}
class User {
companion object : Factory<User> {
override fun create() = User()
}
}
Вопрос
Как получить имя класса во время выполнения?
Ответ
Через ::class.simpleName или ::class.qualifiedName:
println(User::class.simpleName) // "User"
Для экземпляра: obj::class.simpleName.
Корутины (Coroutines)
Вопрос
Что такое корутины в Kotlin?
Ответ
Корутины — это легковесные потоки выполнения, управляемые библиотекой kotlinx.coroutines. Они позволяют писать асинхронный код в синхронном стиле без блокировки потоков.
Вопрос
Чем корутины отличаются от потоков?
Ответ
Потоки управляются операционной системой и требуют значительных ресурсов. Корутины управляются на уровне пользователя, могут быть приостановлены и возобновлены без блокировки потока, и тысячи корутин могут выполняться на одном потоке.
Вопрос
Как запустить корутину?
Ответ
С помощью функций-строителей:
launch— запускает корутину «в фоне», не возвращает результат.async— запускает корутину и возвращаетDeferred<T>, из которого можно получить результат черезawait().
Пример:
GlobalScope.launch {
delay(1000)
println("World")
}
println("Hello")
Вопрос
Что такое CoroutineScope?
Ответ
CoroutineScope определяет контекст и жизненный цикл корутины. Все корутины должны выполняться в каком-либо scope: GlobalScope, viewModelScope, lifecycleScope, или пользовательский.
Вопрос
Почему не рекомендуется использовать GlobalScope?
Ответ
GlobalScope не привязан к жизненному циклу компонента (активности, фрагмента, ViewModel). Это может привести к утечкам памяти и выполнению корутин после уничтожения компонента. Лучше использовать scoped-корутины.
Вопрос
Что такое suspend-функция?
Ответ
suspend-функция — это функция, которую можно приостанавливать и возобновлять. Она может вызываться только из других suspend-функций или корутин:
suspend fun fetchData(): String {
delay(1000) // приостанавливает корутину
return "Data"
}
Вопрос
Может ли suspend-функция выполняться синхронно?
Ответ
Да. Если suspend-функция не содержит точек приостановки (delay, withContext, сетевые вызовы и т.д.), она выполняется синхронно, как обычная функция.
Вопрос
Что такое Dispatchers?
Ответ
Dispatchers определяют, в каком потоке или пуле потоков выполняется корутина:
Dispatchers.Main— основной поток (Android UI).Dispatchers.IO— операции ввода-вывода.Dispatchers.Default— CPU-интенсивные задачи.Dispatchers.Unconfined— запускает корутину в текущем потоке до первой приостановки.
Вопрос
Как переключить контекст выполнения внутри корутины?
Ответ
С помощью withContext:
suspend fun loadAndProcess(): String {
val data = withContext(Dispatchers.IO) {
loadDataFromDisk()
}
return process(data) // выполняется в исходном диспетчере
}
Вопрос
Что такое Job?
Ответ
Job представляет собой задачу, связанную с корутиной. Он позволяет отслеживать состояние (активна, завершена, отменена) и отменять корутину:
val job = launch {
repeat(1000) { i ->
println("Tick $i")
delay(100)
}
}
delay(500)
job.cancel()
Вопрос
Что такое CoroutineContext?
Ответ
CoroutineContext — это набор элементов, определяющих поведение корутины: Job, Dispatcher, CoroutineName, CoroutineExceptionHandler.
Контексты можно комбинировать с помощью оператора +.
Вопрос
Как обрабатывать исключения в корутинах?
Ответ
Для launch — через CoroutineExceptionHandler. Для async — при вызове await():
val handler = CoroutineExceptionHandler { _, exception ->
println("Caught $exception")
}
launch(handler) {
throw RuntimeException("Oops!")
}
Вопрос
Что делает supervisorScope?
Ответ
supervisorScope создаёт scope, в котором сбой одной дочерней корутины не отменяет остальные. Полезно для независимых задач:
supervisorScope {
val task1 = launch { /* ... */ }
val task2 = launch { throw Exception() }
// task1 продолжит работу
}
Вопрос
Что такое Channel?
Ответ
Channel — это потокобезопасная очередь для передачи данных между корутинами. Аналог BlockingQueue, но без блокировки:
val channel = Channel<Int>()
launch {
for (x in 1..5) channel.send(x)
channel.close()
}
launch {
for (y in channel) println(y)
}
Вопрос
Что такое Flow?
Ответ
Flow — это холодный асинхронный поток данных, аналог RxJava Observable, но более простой и интегрированный с корутинами:
fun numbers(): Flow<Int> = flow {
for (i in 1..3) {
delay(100)
emit(i)
}
}
numbers().collect { println(it) }
Вопрос
Чем Flow отличается от Sequence?
Ответ
Sequence — синхронный, ленивый поток. Flow — асинхронный, поддерживает приостановку, работает в корутинах и безопасен для многопоточности.
Вопрос
Как преобразовать suspend-функцию в Flow?
Ответ
С помощью flow или callbackFlow:
fun observeUser(): Flow<User> = flow {
val listener = { user: User -> emit(user) }
addUserListener(listener)
awaitClose { removeUserListener(listener) }
}
Вопрос
Что такое StateFlow и SharedFlow?
Ответ
StateFlow— этоFlow, который хранит последнее значение и выдаёт его новым подписчикам. Аналог LiveData.SharedFlow— многоточечныйFlowбез начального значения, поддерживает буферизацию и ретрансляцию.
Вопрос
Как отменить корутину?
Ответ
Через job.cancel() или coroutineContext.cancel(). Также корутина автоматически отменяется при выходе из scope.
Вопрос
Что такое структурированная параллельность?
Ответ
Это принцип, при котором все дочерние корутины автоматически отменяются, когда завершается их родительский scope. Это предотвращает утечки и упрощает управление жизненным циклом.
Коллекции (продвинутый уровень)
Вопрос
Как отфильтровать список?
Ответ
С помощью функции filter:
val evens = list.filter { it % 2 == 0 }
Вопрос
Как преобразовать каждый элемент списка?
Ответ
С помощью map:
val lengths = names.map { it.length }
Вопрос
Что делает flatMap?
Ответ
flatMap применяет функцию, возвращающую коллекцию, и объединяет результаты в один список:
val words = listOf("a b", "c d")
val all = words.flatMap { it.split(" ") } // ["a", "b", "c", "d"]
Вопрос
Как найти первый элемент по условию?
Ответ
С помощью find (синоним firstOrNull):
val firstPositive = list.find { it > 0 }
Вопрос
Чем first() отличается от firstOrNull()?
Ответ
first() выбрасывает исключение, если элемент не найден. firstOrNull() возвращает null.
Вопрос
Как сгруппировать элементы?
Ответ
С помощью groupBy:
val byLength = words.groupBy { it.length }
Результат — Map<Int, List<String>>.
Вопрос
Как объединить два списка в пары?
Ответ
С помощью zip:
val names = listOf("Alice", "Bob")
val ages = listOf(30, 25)
val pairs = names.zip(ages) // List<Pair<String, Int>>
Вопрос
Как разбить список на части?
Ответ
С помощью chunked:
val chunks = (1..10).toList().chunked(3)
// [[1,2,3], [4,5,6], [7,8,9], [10]]
Вопрос
Как отсортировать список?
Ответ
sorted()— по возрастанию.sortedBy { ... }— по ключу.sortedDescending()— по убыванию.
Вопрос
Что такое associate?
Ответ
associate создаёт Map из коллекции:
val map = list.associateWith { it * 2 }
// [1,2] → {1=2, 2=4}
val map2 = list.associate { it to it.toString() }
Вопрос
Как проверить, что все элементы удовлетворяют условию?
Ответ
С помощью all:
val allPositive = list.all { it > 0 }
Вопрос
Как проверить, что хотя бы один элемент удовлетворяет условию?
Ответ
С помощью any:
val hasNegative = list.any { it < 0 }
Вопрос
Что делает fold?
Ответ
fold сворачивает коллекцию в одно значение:
val sum = list.fold(0) { acc, item -> acc + item }
Вопрос
Чем reduce отличается от fold?
Ответ
reduce не принимает начальное значение — оно берётся из первого элемента коллекции. Коллекция не должна быть пустой.
Вопрос
Как удалить дубликаты?
Ответ
С помощью distinct():
val unique = list.distinct()
Взаимодействие с Java
Вопрос
Как вызвать Kotlin-код из Java?
Ответ
Kotlin-код компилируется в совместимый с Java байт-код. Классы, функции и свойства доступны из Java почти как обычные Java-члены.
Вопрос
Как сделать Kotlin-функцию статической для Java?
Ответ
Поместить её в companion object и добавить @JvmStatic:
class Utils {
companion object {
@JvmStatic fun now() = System.currentTimeMillis()
}
}
В Java: Utils.now().
Вопрос
Как обрабатывать nullable-типы из Java?
Ответ
Kotlin помечает параметры из Java как платформенные типы (String!). Чтобы явно указать nullability, используют аннотации @Nullable и @NotNull в Java-коде или @file:JvmDefault в Kotlin.
Вопрос
Что такое @JvmField?
Ответ
Аннотация @JvmField заставляет компилятор генерировать публичное поле вместо геттера/сеттера:
class Config {
@JvmField val timeout = 5000
}
В Java: config.timeout (а не config.getTimeout()).
Вопрос
Как экспортировать top-level-функцию в Java-класс?
Ответ
По умолчанию top-level-функции помещаются в класс с именем FileNameKt. Чтобы изменить имя, используют @file:JvmName:
// utils.kt
@file:JvmName("StringUtils")
package util
fun isEmpty(s: String) = s.isEmpty()
В Java: StringUtils.isEmpty(...).
Вопрос
Поддерживает ли Kotlin checked exceptions?
Ответ
Нет. Kotlin игнорирует checked exceptions. Однако при вызове Java-метода, бросающего checked exception, Kotlin не требует его обработки.
Вопрос
Как использовать SAM-интерфейсы из Java в Kotlin?
Ответ
Kotlin автоматически преобразует лямбду в Java SAM-интерфейс. Для Kotlin-интерфейсов нужно использовать fun interface.
Вопрос
Что такое @JvmOverloads?
Ответ
Аннотация генерирует перегрузки метода для Java, чтобы поддержать параметры по умолчанию:
@JvmOverloads
fun greet(name: String, title: String = "Mr.") = "Hello, $title $name!"
В Java появятся два метода: greet(String) и greet(String, String).
Вопрос
Как получить доступ к backing field из Java?
Ответ
Backing field недоступен напрямую. Доступ осуществляется через геттер и сеттер, сгенерированные компилятором.
Вопрос
Можно ли наследовать Kotlin-класс из Java?
Ответ
Да, если класс помечен как open. Все open-члены также должны быть переопределимы.
Продвинутые возможности языка
Вопрос
Что такое типобезопасный строитель (type-safe builder)?
Ответ
Это идиома Kotlin, использующая функции-расширения и лямбды с приёмником для создания внутренних DSL. Пример — создание XML или UI:
html {
body {
h1 { +"Hello" }
p { +"World" }
}
}
Каждая функция (html, body и т.д.) принимает лямбду с приёмником, что позволяет писать выразительный и проверяемый на этапе компиляции код.
Вопрос
Что такое лямбда с приёмником?
Ответ
Лямбда с приёмником — это лямбда, внутри которой this ссылается на определённый объект (приёмник). Используется в DSL и функциях вроде apply, with, run:
fun buildString(block: StringBuilder.() -> Unit): String {
val sb = StringBuilder()
sb.block()
return sb.toString()
}
val result = buildString {
append("Hello")
append(" ")
append("World")
}
Вопрос
Как создать свой DSL в Kotlin?
Ответ
Сочетанием:
- extension-функций,
- лямбд с приёмником,
- инфиксных и операторных функций (опционально).
Пример:
class RouteBuilder {
fun get(path: String, handler: () -> Unit) {
// регистрируем маршрут
}
}
fun routes(block: RouteBuilder.() -> Unit): RouteBuilder {
val builder = RouteBuilder()
builder.block()
return builder
}
val app = routes {
get("/users") { /* ... */ }
}
Вопрос
Что такое аннотации в Kotlin?
Ответ
Аннотации — это метаданные, которые можно применять к классам, функциям, свойствам и другим элементам. Они обрабатываются компилятором или во время выполнения через рефлексию:
@Target(AnnotationTarget.FUNCTION)
annotation class Test
@Test
fun myTest() { }
Вопрос
Как объявить аннотацию?
Ответ
С помощью ключевого слова annotation class:
annotation class JsonName(val name: String)
Параметры аннотации должны быть константами времени компиляции.
Вопрос
Что делает @Target?
Ответ
Ограничивает, к каким элементам можно применять аннотацию:
@Target(AnnotationTarget.CLASS, AnnotationTarget.FUNCTION)
annotation class MyAnnotation
Вопрос
Что такое @Retention?
Ответ
Указывает, как долго аннотация сохраняется:
AnnotationRetention.SOURCE— только в исходном коде.AnnotationRetention.BINARY— в байт-коде, но недоступна во время выполнения.AnnotationRetention.RUNTIME— доступна через рефлексию.
По умолчанию — RUNTIME.
Вопрос
Как получить аннотации через рефлексию?
Ответ
С помощью KClass и методов annotations, findAnnotation:
val annotations = MyClass::class.annotations
val json = MyClass::class.findAnnotation<JsonName>()
Требуется зависимость kotlin-reflect.
Вопрос
Что такое @DslMarker?
Ответ
Аннотация, предотвращающая смешивание контекстов в DSL. Она гарантирует, что внутри одного DSL-блока нельзя случайно вызвать методы из другого DSL:
@DslMarker
annotation class HtmlTagMarker
@HtmlTagMarker
abstract class Tag { /* ... */ }
Вопрос
Что такое expect/actual?
Ответ
Механизм мультиплатформенности. expect объявляет API в общем модуле, actual предоставляет реализацию для конкретной платформы (JVM, JS, Native):
// commonMain
expect fun currentTime(): Long
// jvmMain
actual fun currentTime() = System.currentTimeMillis()
Компиляция и инструментарий
Вопрос
Во что компилируется Kotlin?
Ответ
Kotlin может компилироваться в:
- JVM байт-код (основной вариант),
- JavaScript (для фронтенда),
- Нативный бинарник через Kotlin/Native (LLVM).
Вопрос
Поддерживает ли Kotlin Java 8+?
Ответ
Да. Kotlin генерирует байт-код, совместимый с Java 6+, но может использовать API Java 8+ при соответствующей настройке jvmTarget.
Вопрос
Что такое kotlin-stdlib?
Ответ
Это стандартная библиотека Kotlin, содержащая базовые функции (let, apply, коллекции, корутины и т.д.). Подключается автоматически в большинстве проектов.
Вопрос
Нужно ли подключать kotlin-reflect?
Ответ
Только если используется рефлексия (::class, динамический вызов и т.д.). Это отдельная зависимость, не включена по умолчанию из соображений размера.
Вопрос
Как работает совместимость с Java?
Ответ
Kotlin полностью совместим с Java: можно вызывать Java из Kotlin и наоборот без обёрток. Компилятор генерирует совместимый байт-код и учитывает особенности Java (например, checked exceptions игнорируются).
Вопрос
Что такое @JvmDefault?
Ответ
Аннотация, позволяющая использовать реализацию метода по умолчанию в интерфейсах на JVM 8+. Без неё Kotlin генерирует дополнительные статические методы.
Вопрос
Как настроить Gradle для Kotlin?
Ответ
Добавить плагин kotlin("jvm") или kotlin-android, указать зависимости:
plugins {
kotlin("jvm") version "2.0.0"
}
dependencies {
implementation(kotlin("stdlib"))
}
Вопрос
Поддерживает ли Kotlin Lombok?
Ответ
Нет. Lombok работает на уровне Java-компилятора до обработки Kotlin. Вместо этого Kotlin предлагает data class, lateinit, делегаты и другие встроенные средства.
Вопрос
Что такое KAPT?
Ответ
Kotlin Annotation Processing Tool — это адаптер, позволяющий использовать Java-процессоры аннотаций (например, Dagger, Room) в Kotlin-проектах.
Вопрос
Как отладить Kotlin-код?
Ответ
Kotlin поддерживается всеми основными IDE (IntelliJ IDEA, Android Studio). Точки останова, просмотр переменных и шаговое выполнение работают так же, как в Java.
Безопасность и лучшие практики
Вопрос
Как избежать NPE в Kotlin?
Ответ
Использовать:
- ненулевые типы по умолчанию,
- безопасные вызовы (
?.), - оператор Elvis (
?:), - проверки
if (x != null), - избегать
!!.
Вопрос
Когда допустимо использовать !!?
Ответ
Только когда гарантируется, что значение не null (например, после проверки или из доверенного API). В остальных случаях — избегать.
Вопрос
Почему lateinit опасен?
Ответ
Если обратиться к lateinit-свойству до инициализации, будет выброшено UninitializedPropertyAccessException. Его следует использовать только в контролируемых условиях (например, в тестах или фреймворках).
Вопрос
Как сделать код более идиоматичным на Kotlin?
Ответ
- Использовать
valвместоvar. - Предпочитать
whenвместо цепочекif-else. - Использовать extension-функции вместо утилитных классов.
- Применять scope-функции (
let,apply,run). - Избегать явных типов там, где возможен вывод.
Вопрос
Следует ли избегать наследования?
Ответ
Да. Kotlin поощряет композицию и делегирование. Классы по умолчанию final, что соответствует принципу «наследуй только если необходимо».
Вопрос
Как правильно обрабатывать ошибки?
Ответ
В Kotlin предпочитают использовать:
- sealed classes + when (для recoverable ошибок),
- исключения (для unrecoverable ситуаций),
- Result (в функциях, где важна явная обработка).
Вопрос
Что такое Result?
Ответ
Result<T> — это тип-обёртка для представления успешного результата или ошибки:
fun divide(a: Int, b: Int): Result<Int> =
if (b == 0) Result.failure(IllegalArgumentException("Zero"))
else Result.success(a / b)
Вопрос
Можно ли использовать Result в public API?
Ответ
Нет. Result не рекомендуется возвращать из public функций верхнего уровня или в интерфейсах, так как он несовместим с Java и усложняет композицию.
Вопрос
Как обеспечить потокобезопасность?
Ответ
- Использовать неизменяемые структуры данных.
- Применять корутины с правильными диспетчерами.
- Использовать
synchronized,Mutex,AtomicReferenceпри необходимости.
Вопрос
Что такое immutable-first подход?
Ответ
Это практика, при которой по умолчанию используются неизменяемые данные (val, listOf, data class без var). Изменяемость вводится только при явной необходимости.
Экосистема и архитектура
Вопрос
Какие фреймворки популярны на Kotlin?
Ответ
- Android: Jetpack Compose, Koin, Room, Retrofit, Coroutines.
- Backend: Ktor, Spring Boot (с Kotlin), Exposed.
- Multiplatform: Kotlin Multiplatform Mobile (KMM).
Вопрос
Что такое Ktor?
Ответ
Ktor — это лёгкий фреймворк от JetBrains для создания серверных и клиентских приложений на Kotlin. Поддерживает корутины, DSL и мультиплатформенность.
Вопрос
Поддерживает ли Spring Kotlin?
Ответ
Да. Spring Framework и Spring Boot полностью поддерживают Kotlin, включая null safety, data classes и корутины (в реактивных вариантах).
Вопрос
Что такое Arrow?
Ответ
Arrow — это библиотека для функционального программирования на Kotlin. Предоставляет типы Option, Either, IO, законы функторов и монад.
Вопрос
Как организовать слои в Kotlin-приложении?
Ответ
Рекомендуется следовать архитектурным паттернам:
- Clean Architecture (Presentation, Domain, Data),
- MVVM (для Android),
- Hexagonal Architecture (Ports and Adapters).
Использовать sealed class для состояний, Flow для реактивности, Repository для абстракции данных.
Вопрос
Как тестировать Kotlin-код?
Ответ
- Unit-тесты: JUnit 5 + Kotest или Truth.
- Mocking: MockK (нативный мокинг для Kotlin).
- Интеграционные тесты: TestContainers,@SpringBootTest.
Вопрос
Что такое MockK?
Ответ
MockK — это фреймворк для мокинга, разработанный специально для Kotlin. Поддерживает suspend-функции, vararg, extension-функции и корутины.
Вопрос
Как писать документацию к Kotlin-коду?
Ответ
Используется KDoc — аналог Javadoc с расширениями для Kotlin:
/**
* Returns the sum of two integers.
* @param a first operand
* @param b second operand
* @return sum of [a] and [b]
*/
fun add(a: Int, b: Int) = a + b
Вопрос
Поддерживает ли Kotlin модульность (JPMS)?
Ответ
Частично. Kotlin может генерировать module-info.class, но полная поддержка Java Platform Module System требует ручной настройки и ограничена.
Вопрос
Какие преимущества Kotlin перед Java?
Ответ
- Null safety.
- Лаконичный синтаксис.
- Расширяемость через extension-функции.
- Поддержка функционального программирования.
- Современные конструкции (корутины, sealed classes, DSL).
- Полная совместимость с Java.
Вопрос
Есть ли недостатки у Kotlin?
Ответ
- Более медленная компиляция по сравнению с Java (в некоторых сценариях).
- Увеличенный размер APK (из-за stdlib).
- Сложность отладки inline-функций.
- Ограниченная поддержка в некоторых legacy-системах.
Вопрос
Как начать миграцию с Java на Kotlin?
Ответ
- Начать с новых классов на Kotlin.
- Использовать автоматическое преобразование в IntelliJ IDEA.
- Постепенно переписывать утилиты, data-классы, ViewModel.
- Поддерживать двуязычный проект — Kotlin и Java могут сосуществовать.
Вопрос
Что такое Kotlin Script (.kts)?
Ответ
Это исполняемые скрипты на Kotlin, используемые для автоматизации, Gradle-скриптов, REPL. Не требуют компиляции вручную.
Вопрос
Поддерживает ли Kotlin сериализацию?
Ответ
Да. Библиотека kotlinx.serialization предоставляет аннотации @Serializable, поддержку JSON, Protobuf, CBOR и кастомных форматов.
Вопрос
Как работает @Serializable?
Ответ
Аннотация генерирует сериализатор во время компиляции:
@Serializable
data class User(val name: String, val age: Int)
val json = Json.encodeToString(User("Alice", 30))
Вопрос
Что такое multiplatform-проекты?
Ответ
Проекты, где общий код пишется на Kotlin и компилируется под несколько платформ: JVM, JS, iOS, Android, Desktop. Общая логика выносится в commonMain.
Вопрос
Как обмениваться данными между платформами в KMM?
Ответ
Через expect/actual, общие data class, sealed class, Flow, suspend-функции. Платформенный код реализуется в iosMain, androidMain и т.д.
Вопрос
Какие компании используют Kotlin?
Ответ
Google (официальный язык Android), Netflix, Uber, Pinterest, Twitter, JetBrains, Amazon и многие другие.
Вопрос
Является ли Kotlin open source?
Ответ
Да. Компилятор, стандартная библиотека и инструменты находятся в открытом доступе на GitHub под лицензией Apache 2.0.
Вопрос
Как следить за развитием Kotlin?
Ответ
- Официальный сайт: kotlinlang.org
- GitHub: github.com/JetBrains/kotlin
- KEEP (Kotlin Evolution and Enhancement Process)
- Релизные заметки и roadmap от JetBrains
Вопрос
Что такое KEEP?
Ответ
KEEP — это процесс предложения и обсуждения изменений в языке Kotlin. Аналог JEP в Java. Все крупные фичи проходят через публичное обсуждение.
Вопрос
Какие новые фичи появились в Kotlin 2.0?
Ответ
- Улучшенный компилятор (K2) по умолчанию.
- Лучшая поддержка K2 в IDE.
- Ускоренная компиляция.
- Улучшенная обработка ошибок.
- Поддержка новых JVM-фич.
Вопрос
Что такое K2?
Ответ
K2 — это новое поколение компилятора Kotlin, написанное с нуля. Он быстрее, точнее и лучше поддерживает современные фичи языка.
Вопрос
Стоит ли учить Kotlin в 2026 году?
Ответ
Да. Kotlin продолжает расти в мобильной, серверной и мультиплатформенной разработке. Он является стратегическим языком для Android и активно развивается JetBrains и сообществом.
Вопрос
Где применяется Kotlin помимо Android?
Ответ
- Backend (Ktor, Spring Boot)
- Desktop (Compose for Desktop)
- Web (Kotlin/JS, React wrappers)
- Data Science (Kotlin Notebook, krangl)
- Embedded и IoT (Kotlin/Native)
- Game development (via libGDX, KorGE)
Вопрос
Какие книги рекомендуются по Kotlin?
Ответ
- «Kotlin in Action» — Dmitry Jemerov, Svetlana Isakova
- «Programming Kotlin» — Stephen Samuel, Stefan Bocutiu
- Документация на kotlinlang.org
Вопрос
Как подготовиться к собеседованию по Kotlin?
Ответ
- Выучить основы (null safety, функции, классы).
- Понимать корутины и Flow.
- Уметь объяснить разницу между Java и Kotlin.
- Практиковать решение задач на LeetCode/HackerRank на Kotlin.
- Разбираться в архитектуре Android или backend-приложений на Kotlin.
Вопрос
Что спрашивают на senior-собеседованиях по Kotlin?
Ответ
- Глубокое понимание корутин (отмена, исключения, диспетчеры).
- Реализация DSL.
- Внутреннее устройство компилятора (inline, reified, variance).
- Мультиплатформенная разработка.
- Проектирование API на Kotlin.
- Оптимизация и профилирование.
Вопрос
Какие вопросы задают на junior-собеседованиях?
Ответ
- Разница между
valиvar. - Как работает
when. - Что такое
data class. - Как обработать
null. - Простые задачи на коллекции (
filter,map).
Вопрос
Какие типичные ошибки допускают кандидаты?
Ответ
- Использование
!!без необходимости. - Непонимание разницы между
ListиMutableList. - Путаница в
launchvsasync. - Незнание
sealed class. - Отсутствие знаний о scope-функциях.
Вопрос
Как продемонстрировать владение Kotlin?
Ответ
- Писать идиоматичный код (без Java-стиля).
- Использовать возможности языка (делегаты, extension, корутины).
- Объяснять, почему Kotlin безопаснее и выразительнее.
- Показывать опыт в реальных проектах (GitHub, портфолио).
Вопрос
Можно ли использовать Kotlin для алгоритмических задач?
Ответ
Да. Kotlin поддерживается на Codeforces, LeetCode, HackerRank. Его стандартная библиотека упрощает работу с коллекциями и строками.
Вопрос
Какой стиль кода принят в Kotlin?
Ответ
Официальный гайдлайн: kotlinlang.org/docs/coding-conventions.html. Основные правила:
- Использовать
camelCase. - Открывающая скобка на той же строке.
- Пробелы вокруг операторов.
- Избегать лишних скобок.
Вопрос
Что такое reified типы?
Ответ
В inline-функциях параметр типа можно сделать reified, чтобы получить доступ к нему во время выполнения:
inline fun <reified T> Any.castAs(): T? = this as? T
val str = obj.castAs<String>()
Без reified generic-типы стираются (type erasure).
Вопрос
Почему reified работает только в inline-функциях?
Ответ
Потому что inline-функции встраиваются в точку вызова, и компилятор знает конкретный тип. В обычных функциях тип стирается, как в Java.
Вопрос
Как проверить тип во время выполнения?
Ответ
С помощью is:
if (obj is String) {
println(obj.length) // smart cast
}
Для generic-типов — только с reified в inline-функциях.
Вопрос
Что такое Nothing и где он используется?
Ответ
Nothing — это тип без значений. Используется для функций, которые никогда не завершаются:
fun fail(): Nothing = throw RuntimeException()
Nothing является подтипом всех типов, поэтому его можно присвоить любой переменной.
Вопрос
Как работает equals в Kotlin?
Ответ
Для data class equals генерируется автоматически и сравнивает все свойства. Для обычных классов — наследуется от Any, сравнивает ссылки. Можно переопределить.
Вопрос
Что такое structural vs referential equality?
Ответ
==— structural equality (вызываетequals).===— referential equality (сравнение ссылок).
Вопрос
Как клонировать объект в Kotlin?
Ответ
Для data class — через copy(). Для обычных классов — нужно реализовать клонирование вручную (например, через сериализацию или конструктор копирования).
Вопрос
Поддерживает ли Kotlin множественное наследование?
Ответ
Нет. Но можно реализовать несколько интерфейсов с реализацией по умолчанию, что даёт похожий эффект.
Вопрос
Что такое diamond problem и как Kotlin её решает?
Ответ
Diamond problem — конфликт при наследовании одинаковых методов из двух интерфейсов. Kotlin требует явного разрешения через super<A>.method().
Вопрос
Какие есть альтернативы наследованию?
Ответ
- Композиция.
- Делегирование (
by). - Extension-функции.
- Функции высшего порядка.
Вопрос
Что такое value classes?
Ответ
Value classes (введены в Kotlin 1.5) оборачивают одно значение без аллокации объекта во время выполнения:
@JvmInline value class UserId(val id: String)
Полезны для типобезопасности без накладных расходов.
Вопрос
Когда использовать value class?
Ответ
Когда нужно добавить семантику к примитивному типу (ID, Email, Money), но избежать аллокации. Поддерживаются только с одним свойством и без backing field.
Вопрос
Что такое context receivers (Kotlin 2.0+)?
Ответ
Экспериментальная фича для имитации множественного наследования контекста. Позволяет функции получать несколько неявных приёмников:
context(Logging, Database)
fun process() { ... }
Вопрос
Где найти официальные примеры кода на Kotlin?
Ответ
- github.com/Kotlin
- play.kotlinlang.org
- Примеры в документации
- Официальные туториалы по Ktor, Compose, KMM
Вопрос
Какие IDE поддерживают Kotlin?
Ответ
- IntelliJ IDEA (лучшая поддержка)
- Android Studio
- VS Code (с расширением)
- Emacs, Vim (через Language Server)
Вопрос
Что такое Kotlin Playground?
Ответ
Онлайн-редактор на сайте kotlinlang.org, позволяющий запускать Kotlin-код прямо в браузере. Полезен для обучения и демонстрации.
Вопрос
Как сообщить об ошибке в Kotlin?
Ответ
Через YouTrack: youtrack.jetbrains.com/issues/KT. Можно приложить пример кода и описание.
Вопрос
Как участвовать в развитии Kotlin?
Ответ
- Участвовать в обсуждениях KEEP.
- Писать issues и PR в GitHub.
- Создавать библиотеки и инструменты.
- Проводить митапы и писать статьи.
Вопрос
Что делать, если Kotlin не компилируется?
Ответ
- Проверить версии Kotlin и Gradle.
- Очистить кэш (
./gradlew clean). - Обновить IDE.
- Проверить совместимость
jvmTarget. - Включить K2-компилятор явно.
Вопрос
Как ускорить сборку Kotlin-проекта?
Ответ
- Включить K2.
- Использовать incremental compilation.
- Разбить проект на модули.
- Избегать тяжёлых inline-функций.
- Обновить Gradle и Kotlin plugin.
Вопрос
Что такое IR backend?
Ответ
Intermediate Representation — новый промежуточный формат компилятора, используемый для Kotlin/JS и Kotlin/Native. Постепенно заменяет старый JVM backend.
Вопрос
Будет ли Kotlin заменять Java?
Ответ
В новых проектах — часто. В legacy — сосуществует. Kotlin не стремится заменить Java полностью, но становится предпочтительным выбором для новых разработок, особенно на Android.
Вопрос
Какие soft skills важны для Kotlin-разработчика?
Ответ
- Умение читать и объяснять код.
- Понимание trade-offs (производительность vs выразительность).
- Готовность учить новые фичи языка.
- Внимание к деталям (null safety, immutability).
Вопрос
Что запомнить в первую очередь?
Ответ
val>var?.и?:вместо!!whenвместоif-elsedata classдля данных- Корутины для асинхронности
sealed classдля состояний