Как пройти собеседование по Kotlin: топ вопросов
Kotlin прочно занял место основного языка для Android-разработки и активно используется в бэкенде (Ktor, Spring Boot). Собеседование на позицию Kotlin-разработчика — это проверка не только синтаксиса, но и понимания философии языка: null safety, корутин, функционального подхода. В этой статье мы разобрали самые частые вопросы, которые задают на интервью, с примерами кода и пояснениями.
1. Null Safety: как Kotlin борется с NullPointerException
Один из главных плюсов Kotlin — встроенная защита от null. В отличие от Java, где любая ссылка может быть null, в Kotlin типы разделены на nullable и non-null.
Вопрос: чем отличается ? от !!?
? — оператор безопасного вызова. Если объект null, вызов метода или свойства просто возвращает null без исключения.
val name: String? = null
val length = name?.length // null, ошибки нет
!! — утверждение non-null. Вы гарантируете, что значение не null. Если оно null — падает NullPointerException.
val length = name!!.length // NPE при name == null
Вопрос: что такое элвис-оператор ?:?
Позволяет задать значение по умолчанию, если слева null.
val name: String? = null
val display = name ?: "Гость"
Вопрос: как работает let с nullable-типами?
let выполняется только если объект не null. Это идиоматический способ обработки nullable.
name?.let {
println("Имя: $it")
}
2. Корутины: асинхронность без боли
Корутины — это легковесные потоки, которые позволяют писать асинхронный код в императивном стиле. На собеседовании проверяют понимание скоупов, диспатчеров и suspend-функций.
Вопрос: чем launch отличается от async?
launch— запускает корутину, возвращает Job. Не возвращает результат.async— запускает корутину, возвращает Deferred (аналог Future). Результат получаем через.await().
coroutineScope {
val job = launch { delay(100) }
val deferred = async { 42 }
val result = deferred.await() // 42
}
Вопрос: что такое CoroutineContext и диспатчеры?
CoroutineContext — набор элементов, определяющих поведение корутины (диспатчер, Job, имя). Основные диспатчеры:
Dispatchers.Main— для UI (Android).Dispatchers.IO— для ввода-вывода (сеть, файлы).Dispatchers.Default— для CPU-интенсивных задач.
Вопрос: что такое withContext?
Позволяет сменить диспатчер внутри корутины, не меняя скоуп.
suspend fun loadData(): String = withContext(Dispatchers.IO) {
// долгая операция
"Данные"
}
3. Классы и объекты: sealed, data, companion
Kotlin предлагает несколько специальных типов классов, которые упрощают типичные паттерны.
Вопрос: что такое sealed class и когда его использовать?
Sealed class — это абстрактный класс с ограниченным набором подклассов. Все наследники известны на этапе компиляции. Идеален для представления состояний (UI State) или результатов операций.
sealed class Result {
data class Success(val data: String) : Result()
data class Error(val message: String) : Result()
object Loading : Result()
}
fun handleResult(result: Result) {
when (result) {
is Result.Success -> println(result.data)
is Result.Error -> println(result.message)
Result.Loading -> println("Загрузка...")
}
}
Ветка else не нужна — компилятор знает все варианты.
Вопрос: чем data class отличается от обычного класса?
Data class автоматически генерирует equals(), hashCode(), toString(), copy() и деструктуризацию.
data class User(val name: String, val age: Int)
val user = User("Анна", 25)
val (name, age) = user // деструктуризация
Вопрос: зачем нужен companion object?
Аналог статических методов в Java. Позволяет вызывать методы без создания экземпляра класса.
class Config {
companion object {
const val BASE_URL = "https://api.example.com"
fun createDefault() = Config()
}
}
val url = Config.BASE_URL
4. Функции: extension, lambda, inline
Функции — это first-class citizens в Kotlin. Вопросы про extension-функции и лямбды — обязательная часть собеседования.
Вопрос: что такое extension-функция?
Позволяет "добавить" метод к классу без наследования. На самом деле это статическая функция, первым аргументом которой является объект класса.
fun String.isEmail(): Boolean = this.contains("@")
val result = "test@example.com".isEmail() // true
Вопрос: в чем разница между apply, let, with и also?
| Функция | Контекст | Возвращаемое значение |
|---|---|---|
apply | this | объект-получатель |
let | it | результат лямбды |
with | this | результат лямбды |
also | it | объект-получатель |
val user = User().apply { name = "Иван" } // возвращает User
val length