Kotlin собеседование: 5 ловушек и 7 фишек для победы

Онлайн-тренажер Python для начинающих

Изучайте Python легко и без перегрузки теорией. Решайте практические задачи с автоматической проверкой, получайте подсказки на русском языке и пишите код прямо в браузере — без необходимости что-либо устанавливать.

Начать курс

Kotlin: выстрел в яблочко или мимо кассы?

Kotlin — это не просто «Java без бойлерплейта». Это язык, который затащил Google в своё сердце, обогнал Swift по темпам роста и стал обязательным пунктом в вакансиях мобильных разработчиков. Но вот парадокс: многие приходят на собеседование с мыслью «я знаю Java — разберусь», а уходят с красными глазами. Потому что Kotlin — это другой мир: без null, с корутинами и функциями-расширениями, которые ломают шаблоны.

Мы в PythonLib (да, мы про Python, но Kotlin — это must have для современного бэкенда и мобилок) собрали реальный кейс: как пройти собеседование по Kotlin с блеском. Никакой воды — только код, логика и секретные фишки, о которых молчат на курсах.

1. Почему Kotlin — не «просто Java 2.0»?

Первый вопрос, который вам зададут: «В чём принципиальное отличие Kotlin от Java?». Если вы скажете «синтаксис короче» — вас вежливо попросят выйти. Главное отличие — null safety на уровне типов. В Java вы можете забыть проверить объект на null и получить NPE в рантайме. В Kotlin компилятор не даст вам даже скомпилировать код, если есть риск null.

// Java
String name = null;
int length = name.length(); // NPE в рантайме

// Kotlin
var name: String? = null
val length = name?.length ?: 0 // Безопасно: length = 0

Но это — база. Интервьюер копнёт глубже: спросит про !! (non-null assertion), оператор Элвиса ?: и безопасные вызовы ?.. Ваша задача — показать, что вы понимаете: !! — это зло, а ?: — ваш друг.

  • Safe call (?.) — если объект null, возвращает null.
  • Elvis (?:) — если слева null, берём значение справа.
  • !! — используйте только если уверены на 100%, иначе — баг.

2. Extension functions: магия, которую нужно объяснить

Вопрос: «Как добавить метод к классу, который вы не можете изменить?». В Java — паттерн Decorator или Util-класс. В Kotlin — extension functions. Это не реальное изменение класса, а синтаксический сахар, который делает код читаемым.

// Extension function для String
fun String.isEmail(): Boolean {
    return this.contains("@") && this.contains(".")
}

// Использование
val email = "user@pythonlib.ru"
println(email.isEmail()) // true

Интервьюер может спросить: «Можно ли переопределить extension function?». Ответ: нет, это статический вызов, не динамический. Если вы объявите функцию с тем же именем в классе, приоритет у метода класса.

3. Sealed class vs Enum: когда что использовать?

Это классический вопрос на middle-уровне. Enum — для фиксированного набора констант (дни недели, статусы). Sealed class — для ограниченной иерархии типов, где каждый элемент может иметь своё состояние.

sealed class Result {
    data class Success(val data: String) : Result()
    data class Error(val code: Int, val message: String) : Result()
    object Loading : Result()
}

fun handleResult(result: Result) {
    when (result) {
        is Result.Success -> println("Data: ${result.data}")
        is Result.Error -> println("Error ${result.code}: ${result.message}")
        Result.Loading -> println("Loading...")
    }
}

Почему это важно? Потому что when с sealed class — это гарантия, что вы обработали все случаи. Компилятор не даст вам забыть про новый тип, если вы добавите его в sealed class.

4. Корутины: как не провалиться в асинхронность

Тема, на которой ломаются 70% кандидатов. Корутины — это не потоки. Это легковесные «нити», которые могут приостанавливаться без блокировки потока.

suspend fun fetchData(): String {
    delay(1000) // Не блокирует поток!
    return "Data from PythonLib"
}

fun main() = runBlocking {
    launch {
        val result = fetchData()
        println(result)
    }
    println("This runs first!")
}

Интервьюер спросит: «Чем отличается launch от async?». launch — запускает корутину, возвращает Job (без результата). async — возвращает Deferred (с результатом через .await()).

  • runBlocking — только для main функции и тестов.
  • Dispatchers.Default — для CPU-интенсивных задач.
  • Dispatchers.IO — для сети и диска.
  • Dispatchers.Main — для UI (Android).

5. Дженерики: in, out, star projection

Вопрос: «Как сделать ковариантный List в Kotlin?». В Java — List<? extends T>, в Kotlin — List<out T>. Но Kotlin пошёл дальше: есть declaration-site variance.

interface Source<out T> {
    fun nextT(): T // Можно возвращать T
    // fun addT(item: T) // Ошибка! in position запрещён
}

interface Sink<in T> {
    fun add(item: T) // Можно принимать T
    // fun get(): T // Ошибка! out position запрещён
}

Запомните: out — только чтение (производство), in — только запись (потребление). Star projection (*) — «я не знаю тип, но это безопасно».

6. Проверка на прочность: рефлексия и inline-функции

Рефлексия в Kotlin — это KClass и KFunction. Пример:

val kClass = String::class
val functions = kClass.members
println(functions) // Список всех методов String

Но главная фишка — inline-функции с reified типами. Без них вы не можете проверить тип параметра в дженерике во время выполнения.

inline fun <reified T> isType(value: Any): Boolean {
    return value is T
}

fun main() {
    println(isType<String>("Hello")) // true
    println(isType<Int>("Hello"))    // false
}

Без reified компилятор бы стёр тип. Это must-know для тех, кто пишет библиотеки.

7. Финальный аккорд: scope functions (let, run, with, apply, also)

Интервьюер обожает эту тему. Разница между ними — в контексте (this vs it) и возвращаемом значении.

  • let — работает с it, возвращает последнее выражение. Исп

Блоги

Рекомендаций по книгам