Как senior-ы видят ваш Kotlin-код: разбор на реальных примерах
Вы написали код, запушили его в репозиторий и с замиранием сердца ждёте аппрува. Но вместо зелёной галочки прилетает десяток комментариев от старшего разработчика. Знакомо? Если вы пишете на Kotlin, то наверняка сталкивались с ситуацией, когда ваш, казалось бы, идеальный код, после code review превращается в тыкву.
В этой статье мы разберём, какие типичные замечания senior-ов чаще всего встречаются при ревью кода на Kotlin. Мы не будем учить вас синтаксису — мы покажем, как писать так, чтобы ваш код проходил ревью с первого раза. Поехали!
1. Null Safety: «Почему ты не используешь элвис-оператор?»
Одна из главных фишек Kotlin — null safety. Но многие разработчики, особенно пришедшие из Java, продолжают писать код в стиле «проверь-на-null-и-выполни». Senior-ы это видят сразу.
Плохой пример (Java-стайл)
fun getUserName(user: User?): String {
return if (user != null) {
user.name
} else {
"Unknown"
}
}
Хороший пример (Kotlin-стайл)
fun getUserName(user: User?): String {
return user?.name ?: "Unknown"
}
Использование элвис-оператора (?:) и safe call (?.) делает код лаконичным и читаемым. Senior-ы обожают, когда вы экономите строки без потери ясности. Запомните: if (x != null) { ... } else { ... } — это моветон.
2. Extension Functions: «Где твои утилиты?»
Когда вы видите статический утилитный класс в духе StringUtils.capitalize(str), опытный разработчик на Kotlin поморщится. Extension functions — это то, что делает Kotlin таким элегантным.
Плохой пример
object StringUtils {
fun capitalize(text: String): String {
return text.replaceFirstChar { it.uppercase() }
}
}
// Использование
val result = StringUtils.capitalize("hello")
Хороший пример
fun String.capitalizeFirst(): String {
return this.replaceFirstChar { it.uppercase() }
}
// Использование
val result = "hello".capitalizeFirst()
Сеньоры скажут: «Зачем ты создаёшь объект-помойку? Просто расширь класс!» Используйте extension functions — это улучшает читаемость и следует принципу Kotlin best practices.
3. Sealed Classes вместо Enum: «Твой enum устарел»
Enum-ы в Kotlin — это хорошо, но sealed classes дают гораздо больше гибкости. Особенно когда нужно хранить разные типы данных в одной иерархии.
Плохой пример (enum)
enum class Result {
SUCCESS,
ERROR,
LOADING
}
fun handleResult(result: Result) {
when (result) {
Result.SUCCESS -> // ...
Result.ERROR -> // ...
Result.LOADING -> // ...
}
}
Хороший пример (sealed class)
sealed class Result {
data class Success(val data: List<Item>) : Result()
data class Error(val message: String, val code: Int) : Result()
object Loading : Result()
}
fun handleResult(result: Result) {
when (result) {
is Result.Success -> println("Data: ${result.data}")
is Result.Error -> println("Error: ${result.message}")
Result.Loading -> println("Loading...")
}
}
Senior-разработчики оценят, что вы используете sealed classes для представления состояний. Это один из главных советов senior kotlin — делайте иерархии типов явными.
4. Scope Functions: «Ты забыл про let, apply и run»
Scope functions (let, apply, run, with, also) — мощный инструмент, но новички часто либо не используют их вовсе, либо используют неправильно. Senior-ы на code review любят указывать на это.
Плохой пример (без scope functions)
val user = User()
user.name = "John"
user.age = 30
user.email = "john@example.com"
database.insert(user)
Хороший пример (с apply)
val user = User().apply {
name = "John"
age = 30
email = "john@example.com"
}
database.insert(user)
Или используйте let для безопасной работы с nullable-объектами:
user?.let {
println("User name: ${it.name}")
updateUI(it)
}
Запоминайте: apply — для конфигурации объекта, let — для безопасного выполнения кода с non-null значением, run — для вычислений. Это чистый код Kotlin в действии.
5. Data Classes: «Пиши их правильно»
Казалось бы, что может быть проще data class? Но senior-ы находят ошибки и здесь. Главная проблема — использование изменяемых (mutable) полей.
Плохой пример
data class User(var name: String, var age: Int)
Хороший пример
data class User(val name: String, val age: Int)
Почему? Data class генерирует equals(), hashCode() и copy(). Если поля изменяемые, вы можете получить баги с коллекциями (например, HashSet). Senior-ы скажут: «Сделай поля val, а если нужно изменить — используй copy()».
val updatedUser = user.copy(age = 31)
Это один из ключевых Kotlin best practices, который спасёт вас от трудноуловимых ошибок.
6. Когда не нужен when: «Упрости логику»
Иногда разработчики пишут when там, где можно обойтись простым if или даже тернарным оператором. Senior-ы не любят излишнюю сложность.
Плохой пример
fun getStatusText(status: Boolean): String {
return when (status) {
true -> "Active"
false -> "Inactive"
}
}
Хороший пример
fun getStatusText(status: Boolean): String {
return if (status) "Active" else "Inactive"
}
Или ещё лучше — используйте extension property:
val Boolean.statusText: String
get() = if (this) "Active" else "Inactive"
Правило простое: if для двух вариантов, when для