Программирование
Топ-10 вопросов по Kotlin в 2026 году
В 2026 году Kotlin 2.4.0 упрощает разработку благодаря стабилизации новых возможностей и окончательно закрепляет Kotlin Multiplatform в качестве одной из ведущих архитектур.
В 2026 году Kotlin 2.4.0 упрощает разработку благодаря стабилизации новых возможностей и окончательно закрепляет Kotlin Multiplatform в качестве одной из ведущих архитектур для общей логики кроссплатформенных приложений и интеграции локальных моделей искусственного интеллекта.
1. Почему функции-расширения Kotlin разрешаются статически?
Функции-расширения на самом деле не изменяют класс. Они компилируются в статические функции, которым объект-получатель передаётся в качестве первого параметра.
open class Animal class Dog : Animal()
fun Animal.sound() = "Animal"
fun Dog.sound() = "Dog"
fun printSound(animal: Animal) {
println(animal.sound())
}
fun main() {
printSound(Dog())
}
Результат:
Animal
Хотя во время выполнения объект имеет тип Dog, компилятор выбирает Animal.sound(), поскольку тип переменной на этапе компиляции — Animal.
Совет: методы класса используют рантайм полиморфизм, а функции-расширения — нет.
2. Почему copy() выполняет поверхностное копирование?
Сгенерированная функция copy() копирует ссылки, а не сами объекты, на которые они указывают.
data class Address(var city: String)
data class Employee(
val name: String,
val address: Address
)
val emp1 = Employee("John", Address("Delhi"))
val emp2 = emp1.copy()
emp2.address.city = "Mumbai"
println(emp1.address.city)
Результат:
Mumbai
Оба объекта ссылаются на один и тот же экземпляр Address.
Чтобы выполнить глубокое копирование:
val emp2 = emp1.copy(
address = emp1.address.copy()
)
3. В чём разница между == и ===?
Оператор == проверяет структурное равенство и вызывает equals().
Оператор === проверяет ссылочное равенство, то есть указывают ли переменные на один и тот же объект в памяти.
data class User(val name: String)
val u1 = User("Rahul")
val u2 = User("Rahul")
println(u1 == u2)
println(u1 === u2)
Результат:
true false
4. Зачем появился sealed interface?
Запечатанный класс ограничивает набор наследников, но занимает единственную возможность наследования от класса.
Запечатанный интерфейс sealed interface тоже ограничивает набор реализаций, однако позволяет классу одновременно наследоваться от другого класса и реализовывать несколько интерфейсов.
sealed interface Payment
open class BaseResponse
class CardPayment :
BaseResponse(),
Payment
Такую комбинацию нельзя реализовать с помощью запечатанного класса, поскольку Kotlin разрешает наследоваться только от одного класса.
5. Почему функция-расширение не может переопределить метод класса?
Методы класса всегда имеют приоритет.
class Person {
fun greet() = "Member"
}
fun Person.greet() = "Extension"
fun main() {
println(Person().greet())
}
Результат:
Member
Компилятор всегда выбирает настоящий метод класса.
6. Почему последовательности могут быть быстрее коллекций при обработке больших объёмов данных?
Операции над коллекциями выполняются энергично — каждая операция сразу обрабатывает все элементы.
list.filter {}
.map {}
.take(5)
Каждая операция может создавать промежуточную коллекцию.
Последовательности обрабатываются лениво.
list.asSequence()
.filter {}
.map {}
.take(5)
.toList()
Элементы обрабатываются по одному, что позволяет снизить расход памяти и избежать лишней работы.
7. Зачем нужен fold(), если уже есть reduce()?
Функция reduce() использует первый элемент коллекции как начальное значение аккумулятора и завершается с ошибкой для пустого списка.
listOf<Int>().reduce { acc, i ->
acc + i
}
Этот код выбросит исключение.
Функция fold() принимает явно заданное начальное значение.
listOf<Int>().fold(0) { acc, i ->
acc + i
}
Результат:
0
Благодаря этому fold() безопасно работает с пустыми коллекциями.
8. Почему StateFlow всегда передаёт последнее значение?
StateFlow всегда хранит текущее состояние.
private val _state = MutableStateFlow(0)
val state = _state.asStateFlow()
Когда новый подписчик начинает собирать значения, он сразу получает последнее состояние.
В отличие от SharedFlow, StateFlow предназначен именно для представления состояния приложения или пользовательского интерфейса.
9. Почему reified требует inline?
Обычно информация о параметрах обобщённых типов стирается во время выполнения.
fun <T> printType() {
}
Во время выполнения программа не знает, какой конкретный тип скрывается за T.
Модификатор inline позволяет компилятору подставить тело функции непосредственно в место её вызова и заменить T фактическим типом.
inline fun <reified T> printType() {
println(T::class.simpleName)
}
Теперь вызов:
printType<String>()
выведет:
String
10. Действительно ли val означает неизменяемость?
Нет.
val делает неизменяемой ссылку, но не обязательно сам объект.
val numbers = mutableListOf(1, 2, 3) numbers.add(4)
Такой код корректен, поскольку сам список остаётся изменяемым.
Но следующий код не скомпилируется:
numbers = mutableListOf()
Причина в том, что переменная numbers объявлена через val, поэтому ей нельзя присвоить другую ссылку.
Для настоящей неизменяемости следует использовать неизменяемые структуры данных или предоставлять наружу интерфейсы только для чтения.
Дополнительный вопрос: что такое контекстные ресиверы в Kotlin и зачем они появились?
Контекстные получатели (Context Receivers) позволяют функции или свойству объявить, что для их вызова в текущей области видимости должны быть доступны один или несколько контекстных объектов.
Вместо явной передачи зависимостей через параметры функции нужный контекст предоставляется неявно.
Без контекстных получателей:
class Logger {
fun log(message: String) {
println(message)
}
}
fun printUser(logger: Logger, name: String) {
logger.log("User: $name")
}
Каждая функция должна явно принимать параметр Logger.
С контекстными получателями:
class Logger {
fun log(message: String) {
println(message)
}
}
context(Logger)
fun printUser(name: String) {
log("User: $name")
}
fun main() {
val logger = Logger()
with(logger) {
printUser("Rahul")
}
}
Теперь функция printUser() автоматически получает доступ к Logger из текущего контекста.
-
Аналитика магазинов4 недели назадМобильный рынок Ближнего Востока: выручка растёт быстрее загрузок: исследование Bidease и Sensor Tower
-
Новости4 недели назадВидео и подкасты о мобильной разработке 2026.24
-
Разработка4 недели назадЧто нового в SwiftUI после WWDC26
-
Разработка4 недели назадЧто нового в UIKit после WWDC26
