Модернізація застарілих систем — це складне завдання для багатьох організацій, особливо коли йдеться про великі моноліти, написані на Java. Одним із найбільш ефективних і безпечних підходів є поступова міграція на Kotlin. Це дозволяє працювати зі старим кодом, поки нові функціональності, рефакторинги та тести розробляються на Kotlin.
Kotlin повністю сумісний з Java, що дозволяє продовжувати використовувати старий код і бібліотеки, не переписуючи систему з нуля. Поступова адаптація дає змогу отримати переваги від сучасної мови без ризиків і проблем, пов'язаних з великою перепискою коду.
Переваги Kotlin
- Лаконічність: Код стає зрозумілішим і менш об'ємним, що зменшує кількість шаблонного коду.
- Безпека від null: Вдосконалена система типів допомагає уникнути проблем із
NullPointerException
. - Взаємодія з Java: Легке використання бібліотек Java і існуючого коду без складнощів.
- Продуктивність: Завдяки сучасному синтаксису і функціям розробка прискорюється.
- Корутіни: Легке та ефективне асинхронне програмування, яке зберігає простоту коду.
- Функції-розширення: Дозволяють розширювати функціональність існуючих класів без змін в їх коді.
- Потужні утилітарні функції: Функції для роботи з колекціями, такі як
partition
,groupBy
,mapNotNull
, роблять маніпуляцію даними виразнішою. - Комбінація ООП і функціонального програмування: Конструкції, як
data class
,sealed class
, лямбда-вирази, допомагають зробити код більш надійним і зрозумілим.
Приклад: фільтрація та трансформація списку
Java:
List nomes = Arrays.asList("Ana", "Bruno", "Carlos", "Bea");
List resultado = nomes.stream()
.filter(nome -> nome.startsWith("B"))
.map(String::toUpperCase)
.collect(Collectors.toList());
Kotlin:
val nomes = listOf("Ana", "Bruno", "Carlos", "Bea")
val resultado = nomes.filter { it.startsWith("B") }.map { it.uppercase() }
Реальний випадок: міграція поетапно
У компанії, де я працював, ми поступово почали впроваджувати Kotlin в старий моноліт на Java. На перших етапах ми зберегли старі класи на Java і почали писати нові функціональності на Kotlin. Це дозволило нам значно покращити продуктивність, зручність читання коду і його загальну якість.
Більше організації та кращі тести
Kotlin дозволив нам значно покращити структуру коду, чітко розділяючи відповідальності класів, а також полегшив написання тестів. Створення тестових об'єктів стало набагато зручнішим завдяки синтаксису Kotlin, а особливо завдяки використанню data class
.
Приклад: Java POJO vs Kotlin data class
Java:
public class Pessoa {
private String nome;
private int idade;
public Pessoa(String nome, int idade) {
this.nome = nome;
this.idade = idade;
}
// getters, setters, toString, equals, hashCode...
}
Kotlin:
data class Pessoa(val nome: String, val idade: Int)
Ми також застосовували безпечний підхід до рефакторингу: спочатку писали тести для поточної поведінки, а вже потім вносили зміни. Це дозволило підвищити надійність і уникнути регресій.
Безперервна реструктуризація
Як команда, ми погодилися, що кожного разу, коли потрібно змінити старий код на Java, ми будемо переписувати його на Kotlin, зосереджуючи увагу на полегшенні читання і зрозумілості коду. Це допомогло нам поступово скорочувати розмір великих і складних для підтримки класів.
З часом Kotlin став стандартом для нових розробок: його легше писати, підтримувати, а також значно підвищується продуктивність.
Продуктивність без ускладнень
Ще одна перевага використання Kotlin проявилася при роботі з складними ендпоінтами, що виконували кілька завдань по черзі. Замість використання черг чи важкої архітектури ми використовували корутіни для паралелізації запитів до бази даних і зовнішніх сервісів. Це дозволило зберегти чистоту коду і ефективність роботи.
Приклад: паралельні виклики
Java (CompletableFuture):
public Dashboard getDashboard() throws ExecutionException, InterruptedException {
CompletableFuture userFuture = CompletableFuture.supplyAsync(() -> getUser());
CompletableFuture ordersFuture = CompletableFuture.supplyAsync(() -> getOrders());
CompletableFuture notificationsFuture = CompletableFuture.supplyAsync(() -> getNotifications());
CompletableFuture statsFuture = CompletableFuture.supplyAsync(() -> getDashboardStats());
CompletableFuture.allOf(userFuture, ordersFuture, notificationsFuture, statsFuture).join();
User user = userFuture.get();
List orders = ordersFuture.get();
List notifications = notificationsFuture.get();
DashboardStats stats = statsFuture.get();
return new Dashboard(user, orders, notifications, stats);
}
Kotlin (корутіни):
suspend fun getDashboard(): Dashboard = coroutineScope {
val user = async { getUser() }
val orders = async { getOrders() }
val notifications = async { getNotifications() }
val stats = async { getDashboardStats() }
return Dashboard(
user = user.await(),
orders = orders.await(),
notifications = notifications.await(),
stats = stats.await()
)
}
Функції-розширення: суперсила
Багато разів ми стикалися з необхідністю додати функціональність до класів, написаних іншими командами чи сторонніми бібліотеками. З Java це зазвичай означало дублювання коду в різних місцях.
З функціями-розширеннями в Kotlin можна розширювати функціональність класів, не змінюючи їх код, що робить систему більш модульною та зручною для повторного використання.
Приклад: розширення String
Java:
public class Util {
public static String capitalize(String input) {
return input.substring(0, 1).toUpperCase() + input.substring(1);
}
}
String nome = Util.capitalize("kotlin");
Kotlin:
fun String.capitalizeFirst(): String =
replaceFirstChar { it.uppercase() }
val nome = "kotlin".capitalizeFirst()
Прощавай, NullPointerException
З Kotlin ми можемо явно вказувати, коли змінна може бути null, і компілятор одразу повідомляє нас про це на етапі розробки. Це усунуло одну з найбільш поширених помилок у Java і значно підвищило нашу впевненість у роботі програми.
Приклад: синтаксис безпеки від null
Java:
public String getUserName(User user) {
if (user != null && user.getProfile() != null) {
return user.getProfile().getName();
}
return "Desconhecido";
}
Kotlin:
fun getUserName(user: User?): String {
return user?.profile?.name ?: "Desconhecido"
}
Висновки
Міграція на Kotlin була не лише зміною мови, а й зміною способу мислення, написання та підтримки програм. Ми робили це поступово, з тестами і злагодженістю команди, і отримали надійну, модульну систему, яку легко розвивати. Це був неймовірно корисний і задовольняючий досвід.
Перекладено з: 🔄 Evoluindo Sistemas Legados com Kotlin em Ambiente Híbrido