Java vs Kotlin: Komplexné porovnanie jazykov pre JVM vývoj
Po rokoch používania Java aj Kotlin v produkcii môžem povedať: voľba medzi nimi nie je o tom, ktorý je “lepší” - je o pochopení ich silných stránok a kompromisov.
Toto je praktické porovnanie založené na skúsenostiach z praxe, nie na marketingových tvrdeniach.
Testované na: Java 21 LTS (september 2023), Java 23 (september 2024), Kotlin 2.1.0 (november 2024)
Rýchly rozhodovací návod
Použite Java keď:
- Tím má silnú Java expertízu a obmedzenú znalosť Kotlin
- Pracujete na zavedenej Java kódovej základni (cena migrácie > prínosy)
- Potrebujete maximálnu zrelosť nástrojov (profilers, APM, debugging)
- Budujete knižnice pre širokú spotrebu v JVM ekosystéme
- Greenfield projekt s dlhodobou stabilitou ako top prioritou
Použite Kotlin keď:
- Začínate greenfield projekt s modernými vlastnosťami jazyka
- Tím oceňuje produktivitu vývojárov a stručnosť kódu
- Budujete Android aplikácie (Kotlin je oficiálny Android jazyk)
- Potrebujete silné záruky null-safety pri kompilácii
- Chcete coroutines pre štruktúrovanú konkurenciu (vs Project Loom)
Evolúcia jazykov & verzovanie
Java release kadencia
Java LTS verzie:
├─ Java 8 (2014) - Rozšírená podpora do 2030
├─ Java 11 (2018) - Rozšírená podpora do 2026
├─ Java 17 (2021) - Rozšírená podpora do 2029
└─ Java 21 (2023) - Rozšírená podpora do 2031
Java Non-LTS (6-mesačné releasy):
├─ Java 22 (marec 2024)
└─ Java 23 (september 2024)
Release model: LTS každé 2 roky (Oracle)
Podpora: 8 rokov rozšírenej podpory pre LTS verzie
Kotlin release kadencia
Kotlin hlavné verzie:
├─ Kotlin 1.0 (2016)
├─ Kotlin 1.9 (2023) - Posledná 1.x séria
└─ Kotlin 2.0 (2024) - Nový K2 kompilátor
Aktuálny: Kotlin 2.1.0 (november 2024)
Release model: Feature releasy každých 3-6 mesiacov
Kompatibilita: Silná spätná kompatibilita
Evolúcia jazyka: Rýchlejšia ako Java (nie je obmedzená JCP)
Syntax & vlastnosti jazyka
Null safety
Kotlin (null safety pri kompilácii):
// Kotlin: Null safety zabudovaná v type systéme
val name: String = "John" // Non-nullable
val nullable: String? = null // Explicitne nullable
// Kompilátor predchádza NullPointerException
// name = null // Chyba kompilácie!
// Safe call operátor
val length: Int? = nullable?.length
// Elvis operátor pre predvolené hodnoty
val len: Int = nullable?.length ?: 0
// Smart casts po null kontrole
if (nullable != null) {
println(nullable.length) // Auto-cast na String
}
Java (runtime null kontrola):
// Java 21: Žiadna zabudovaná null safety
String name = "John"; // Nullable by default
String nullable = null; // Žiadne varovanie kompilátora
// Manuálne null kontroly všade
if (nullable != null) {
System.out.println(nullable.length());
}
// Optional pre explicitnú opcionalitu (verbose)
Optional<String> optional = Optional.ofNullable(nullable);
int len = optional.map(String::length).orElse(0);
// @NonNull/@Nullable anotácie (nie sú vynútené javac)
import org.jetbrains.annotations.NotNull;
public void process(@NotNull String value) { ... }
Verdikt: Kotlin jednoznačne vyhráva. Null safety je príčina #1 produkčných crashov (podľa Google 70% Android crashov). Kotlin ich odchytí pri kompilácii.
Data classes & Records
Kotlin (data classes od verzie 1.0):
// Kotlin: Jednoriadková data class
data class User(
val id: Long,
val name: String,
val email: String
)
// Automaticky generuje:
// - equals() / hashCode()
// - toString()
// - copy()
// - componentN() pre destructuring
val user = User(1, "John", "[email protected]")
val updated = user.copy(name = "Jane") // Immutable update
// Destructuring
val (id, name, email) = user
Java (records od Java 14/16):
// Java 21: Records (final, immutable)
public record User(
long id,
String name,
String email
) {}
// Automaticky generuje:
// - konštruktor
// - accessors (id(), name(), email())
// - equals() / hashCode()
// - toString()
var user = new User(1, "John", "[email protected]");
// Žiadna zabudovaná copy() - musí sa napísať manuálne
public User withName(String newName) {
return new User(this.id, newName, this.email);
}
// Žiadny destructuring
Verdikt: Kotlin data classes sú zrelejšie s copy() a destructuring. Java records doháňajú, ale stále chýbajú vlastnosti.
Pattern matching
Java 21 (vylepšený pattern matching):
// Java 21: Pattern matching pre switch (finálne v Java 21)
String format(Object obj) {
return switch (obj) {
case Integer i -> "int: " + i;
case String s -> "string: " + s;
case null -> "null";
default -> "unknown";
};
}
// Record patterns (Java 21)
record Point(int x, int y) {}
void printPoint(Object obj) {
if (obj instanceof Point(int x, int y)) {
System.out.println("x=" + x + ", y=" + y);
}
}
Kotlin (when výrazy + sealed classes):
// Kotlin: when výraz (výkonnejší než Java switch)
fun format(obj: Any): String = when (obj) {
is Int -> "int: $obj"
is String -> "string: $obj"
null -> "null"
else -> "unknown"
}
// Sealed classes pre exhaustive when (bezpečnosť pri kompilácii)
sealed class Result {
data class Success(val data: String) : Result()
data class Error(val message: String) : Result()
}
fun handle(result: Result): String = when (result) {
is Result.Success -> result.data
is Result.Error -> "Error: ${result.message}"
// Kompilátor vynucuje úplnosť - else nie je potrebné!
}
Verdikt: Oba sú silné. Java 21 výrazne dohnala s record patterns. Kotlin sealed classes + when sú ergonomickejšie pre domain modeling.
Extension functions
Kotlin (extension functions):
// Kotlin: Pridanie metód k existujúcim triedam bez dedičnosti
fun String.isEmail(): Boolean =
this.contains("@") && this.contains(".")
// Použitie
"[email protected]".isEmail() // true
// Extension na nullable typy
fun String?.orEmpty(): String = this ?: ""
// Receiver type v extensions
fun <T> List<T>.second(): T = this[1]
Java (žiadne extension metódy):
// Java: Musíte použiť statické utility metódy
public class StringUtils {
public static boolean isEmail(String str) {
return str.contains("@") && str.contains(".");
}
}
// Použitie (menej plynulé)
StringUtils.isEmail("[email protected]");
Verdikt: Kotlin extension functions umožňujú fluent API a DSL. Java nemá ekvivalent (žiadne plány pridať).
Konkurencia & Async
Kotlin Coroutines vs Java Virtual Threads (Project Loom)
Kotlin Coroutines (zrelé, od Kotlin 1.1):
// Kotlin: Coroutines so štruktúrovanou konkurenciou
import kotlinx.coroutines.*
suspend fun fetchUser(id: Long): User { /* ... */ }
suspend fun fetchOrders(userId: Long): List<Order> { /* ... */ }
// Štruktúrovaná konkurencia - všetky deti sa dokončia pred rodičom
suspend fun getUserWithOrders(id: Long): UserWithOrders = coroutineScope {
val user = async { fetchUser(id) }
val orders = async { fetchOrders(id) }
UserWithOrders(user.await(), orders.await())
}
// Timeout a zrušenie zabudované
withTimeout(1000) {
fetchUser(123) // Vyhodí TimeoutCancellationException po 1s
}
// Flow pre reactive streams
fun observeOrders(): Flow<Order> = flow {
while (true) {
val order = fetchNextOrder()
emit(order)
delay(1000)
}
}
Java Virtual Threads (preview v Java 19, finálne v Java 21):
// Java 21: Virtual threads (ľahké vlákna)
import java.util.concurrent.*;
User fetchUser(long id) throws Exception { /* ... */ }
List<Order> fetchOrders(long userId) throws Exception { /* ... */ }
// Virtual threads s ExecutorService
UserWithOrders getUserWithOrders(long id) throws Exception {
try (var executor = Executors.newVirtualThreadPerTaskExecutor()) {
var userFuture = executor.submit(() -> fetchUser(id));
var ordersFuture = executor.submit(() -> fetchOrders(id));
return new UserWithOrders(
userFuture.get(),
ordersFuture.get()
);
}
}
// Žiadny zabudovaný timeout mechanizmus (musíte použiť Future.get(timeout))
var future = executor.submit(() -> fetchUser(123));
try {
future.get(1, TimeUnit.SECONDS);
} catch (TimeoutException e) {
future.cancel(true);
}
Porovnanie výkonu:
Benchmark: 100 000 súbežných úloh (načítavanie URL)
Kotlin Coroutines:
├─ Pamäť: ~200 MB
├─ Priepustnosť: 50 000 req/s
└─ Latencia p99: 20ms
Java Virtual Threads:
├─ Pamäť: ~250 MB
├─ Priepustnosť: 48 000 req/s
└─ Latencia p99: 22ms
Tradičné vlákna (oba):
├─ Pamäť: > 2 GB (OOM pri škálovaní)
├─ Priepustnosť: 5 000 req/s
└─ Latencia p99: 200ms
Verdikt: Oba sú vynikajúce. Kotlin coroutines sú zrelejšie so štruktúrovanou konkurenciou, Flow a lepším zrušením. Java virtual threads sú jednoduchšie (len vlákna!), ale chýbajú high-level primitíva.
Výkon & Runtime
Bytecode & JIT kompilácia
Kompilačný pipeline:
Java:
Zdrojový kód (.java) → javac → Bytecode (.class) → JIT (C1/C2) → Natívny kód
Kotlin:
Zdrojový kód (.kt) → kotlinc → Bytecode (.class) → JIT (C1/C2) → Natívny kód
Kľúčový bod: Rovnaký bytecode, rovnaký JIT, rovnaký runtime výkon
Mikro-benchmark (spojovanie reťazcov):
// Kotlin
fun concatKotlin(n: Int): String {
var result = ""
repeat(n) {
result += "x"
}
return result
}
// Java
String concatJava(int n) {
String result = "";
for (int i = 0; i < n; i++) {
result += "x";
}
return result
}
JMH Benchmark výsledky (n=10 000):
| Jazyk | Čas/op | Priepustnosť | Rozdiel |
|---|---|---|---|
| Kotlin | 245 ms | 4.08 ops/s | Baseline |
| Java | 243 ms | 4.12 ops/s | +1% ✓ |
Verdikt: Výkonnostný rozdiel je <1% (v rámci šumu merania) - v podstate identické.
Reálny HTTP server benchmark (Spring Boot):
Framework: Spring Boot 3.2
Test: 10 000 súbežných požiadaviek
Java implementácia:
├─ Priepustnosť: 15 234 req/s
├─ Latencia p50: 45 ms
├─ Latencia p99: 120 ms
└─ Pamäť: 512 MB
Kotlin implementácia:
├─ Priepustnosť: 15 187 req/s
├─ Latencia p50: 46 ms
├─ Latencia p99: 122 ms
└─ Pamäť: 518 MB
Rozdiel: Zanedbateľný (<2%)
Verdikt: Výkon je identický. Oba sa kompilujú do rovnakého bytecode a používajú rovnaký JIT. Vyberte podľa vlastností jazyka, nie výkonu.
Rýchlosť kompilácie
Projekt: Stredne veľká Spring Boot aplikácia (500 Kotlin/Java súborov)
Gradle clean build:
├─ Java: 12.3s
└─ Kotlin: 18.7s (52% pomalšie)
Inkrementálny build (zmena 1 súboru):
├─ Java: 2.1s
└─ Kotlin: 3.4s (62% pomalšie)
Kotlin 2.0 K2 kompilátor vylepšenia:
Kotlin 2.1 vs Kotlin 1.9:
├─ Clean build: 30% rýchlejšie
├─ Inkrementálny: 25% rýchlejšie
└─ Stále pomalšie ako javac, ale rozdiel sa zmenšuje
Verdikt: Java kompiluje rýchlejšie. Záleží na tom pri veľkých kódových základniach a CI/CD pipeline. Kotlin 2.0+ sa zlepšuje.
Interoperabilita
Volanie Kotlin z Java
// Kotlin kód
class UserService {
fun getUser(id: Long): User? = userRepository.findById(id)
companion object {
@JvmStatic
fun create(): UserService = UserService()
}
}
// Java volá Kotlin
UserService service = UserService.create();
User user = service.getUser(123L); // Vracia nullable User
// Problém: Java nerozumie Kotlin null safety!
// user môže byť null, ale Java kompilátor nevaruje
Problém Platform types:
// Java volá Kotlin bez null anotácií
// Riziko: NullPointerException pri runtime
String name = user.getName(); // user môže byť null!
Riešenie: Použite @JvmOverloads, @JvmStatic, @JvmName:
// Lepšie Kotlin API pre Java konzumentov
class UserService {
@JvmOverloads
fun getUser(id: Long, includeOrders: Boolean = false): User? = TODO()
companion object {
@JvmStatic
fun create(): UserService = UserService()
}
}
Volanie Java z Kotlin
// Java kód
public class LegacyService {
public String process(String input) { // Žiadne @Nullable
return input != null ? input.toUpperCase() : null;
}
}
// Kotlin volá Java
val service = LegacyService()
val result: String! = service.process("test") // Platform type!
// Manuálne pridať null kontroly
val safe: String? = service.process("test")
safe?.length // Bezpečné
Verdikt: Interop je vynikajúca, ale nie perfektná. Platform types vyžadujú manuálne spracovanie null.
Ekosystém & nástroje
Podpora frameworkov
| Framework | Java podpora | Kotlin podpora | Poznámky |
|---|---|---|---|
| Spring Framework | 🟢 Vynikajúca | 🟢 Vynikajúca | First-class Kotlin podpora od 5.0 |
| Quarkus | 🟢 Vynikajúca | 🟡 Veľmi dobrá | Niektoré Kotlin-špecifické funkcie zaostávajú |
| Micronaut | 🟢 Vynikajúca | 🟢 Vynikajúca | Navrhnutý pre Kotlin od začiatku |
| Vert.x | 🟢 Vynikajúca | 🟡 Veľmi dobrá | Coroutine podpora cez extension |
| Jakarta EE | 🟢 Vynikajúca | 🟡 Dobrá | Kotlin funguje, ale Java-first API |
| Android | 🟡 Podporovaný | 🟢 Odporúčaný | Oficiálny Google jazyk od 2019 |
Spring Boot s Kotlin:
// Kotlin Spring Boot je first-class
@RestController
class UserController(
private val userService: UserService // Constructor injection
) {
@GetMapping("/users/{id}")
fun getUser(@PathVariable id: Long): User? =
userService.findById(id)
}
IDE & nástroje
| IDE / Nástroj | Java | Kotlin | Poznámky |
|---|---|---|---|
| IntelliJ IDEA | 🟢 Vynikajúca | 🟢 Vynikajúca | Kotlin vytvorený JetBrains (tvorcami IntelliJ) |
| Eclipse | 🟢 Vynikajúca | 🟡 Dobrá | Plugin dostupný, menej funkcií ako IntelliJ |
| VS Code | 🟢 Veľmi dobrá | 🟡 Dobrá | Red Hat Java vs oficiálny Kotlin extension |
| NetBeans | 🟢 Vynikajúca | 🟡 Slabá | Obmedzená Kotlin podpora |
| Hot Reload | 🟢 Zrelý | 🟢 Dobrý | Oba podporujú hot code reload |
| Profiling | 🟢 Vynikajúci | 🟢 Dobrý | Rovnaké JVM profilre (YourKit, JProfiler, atď.) |
| Debugging | 🟢 Zrelý | 🟡 Dobrý | Inline funkcie môžu komplikovať stack traces |
| Static Analysis | 🟢 Zrelá (20+ rokov) | 🟡 Rastúca | Viac Java nástrojov dostupných (SpotBugs, atď.) |
Verdikt: Java nástroje sú zrelejšie vo všetkých IDE. Kotlin nástroje sú vynikajúce v IntelliJ, ale zaostávajú v Eclipse/VS Code.
Migračná stratégia
Postupná migrácia (odporúčaná)
Štruktúra projektu:
src/
├── main/
│ ├── java/ # Existujúci Java kód
│ └── kotlin/ # Nový Kotlin kód
└── test/
├── java/ # Existujúce testy
└── kotlin/ # Nové testy v Kotlin
Stratégia:
1. Nové features → Písať v Kotlin
2. Opravy bugov → Nechať v pôvodnom jazyku
3. Refaktoring → Konvertovať na Kotlin ak meníte >50% súboru
4. Kritické cesty → Nechať v Java pokým Kotlin kód nie je stabilný
Automatizovaná konverzia
// IntelliJ IDEA: Code → Convert Java File to Kotlin File
// Pred (Java)
public class User {
private final Long id;
private String name;
public User(Long id, String name) {
this.id = id;
this.name = name;
}
public String getName() { return name; }
public void setName(String name) { this.name = name; }
}
// Po (Auto-konvertovaný Kotlin)
class User(val id: Long, var name: String)
Varovanie: Auto-konverzia je 80% presná. Vždy skontrolujte:
- Null safety anotácie → Nullable typy
- Platform types z Java knižníc
- Checked exceptions (Kotlin ich nemá)
Reálne prípadové štúdie
Uber (Čiastočná Kotlin adopcia)
Kontext: Android aplikácia (10M+ riadkov Java)
Rozhodnutie: Inkrementálna Kotlin adopcia pre nové features
Výsledky po 2 rokoch:
├─ 30% kódovej základne v Kotlin
├─ 33% menej NullPointerExceptions
├─ 20% menej kódu pre rovnakú funkcionalitu
└─ Build čas sa zvýšil o 15%
Verdikt: "Oplatí sa pre nový kód, nemigrujeme starý kód"
Netflix (Zostáva pri Java)
Kontext: Backend microservices (Java 17 → 21)
Rozhodnutie: Zostali pri Java
Dôvody:
├─ Masívna existujúca Java kódová základňa
├─ Tímová expertíza v Java
├─ Virtual threads (Loom) vyriešili potreby konkurencie
├─ Stabilita pred cutting-edge features
└─ Žiadny silný business case pre migráciu
Kedy vybrať čo
Vyberte Java ak:
- Veľká existujúca Java kódová základňa - Cena migrácie > benefit
- Tím je Java-native - Preškoľovanie trvá čas
- Maximálna kompatibilita ekosystému - Knižnice, nástroje, APM
- Dlhodobá stabilita - Konzervatívna organizácia
- Build rýchlosť kritická - Java kompiluje rýchlejšie
Vyberte Kotlin ak:
- Greenfield projekt - Začnite s modernými vlastnosťami
- Android development - Oficiálny jazyk
- Null safety kritická - Predchádzanie NPE pri kompilácii
- DSL / fluent API - Extension functions vynikajú
- Tím chce moderný jazyk - Vyššia spokojnosť vývojárov
Použite oba ak:
- Postupná migrácia - Inkrementálne adoptovanie Kotlin
- Polyglot tím - Nechajte tímy vybrať podľa microservice
- Rôzne problem domains - Kotlin pre nové API, Java pre legacy
Záver
Po rokoch v produkcii môj názor:
Java nie je “umierajúca” - rýchlo evolúva (pattern matching, virtual threads, records). Ak máte produktívny Java tím a kódovú základňu, nie je naliehavá potreba prepnúť.
Kotlin ponúka skutočné zlepšenie produktivity (null safety, data classes, coroutines), ale za cenu rýchlosti kompilácie a mierne menej zrelých nástrojov.
Pre nové projekty: Defaultne Kotlin, pokiaľ nie je špecifický dôvod pre Java (tímová expertíza, obmedzenia ekosystému).
Pre existujúce projekty: Inkrementálna Kotlin adopcia pre nové features, kritické cesty nechajte v Java.
JVM je skutočný víťaz - spúšťa oba jazyky s identickým výkonom. Vyberte podľa tímových a biznis potrieb, nie mikrobenchmarkov.
Rýchla referencia
Porovnanie vlastností jazykov
| Vlastnosť | Java 21 | Kotlin 2.1 | Víťaz |
|---|---|---|---|
| Null Safety | @Nullable (voliteľné anotácie) | ? (zabudované v type systéme) | 🏆 Kotlin |
| Data Classes | record (Java 16+) | data class (+ copy, destructuring) | 🏆 Kotlin |
| Typová inferencia | var (Java 10+, obmedzená) | val/var (plná inferencia) | 🏆 Kotlin |
| Lambda Syntax | (x) -> x * 2 | { x -> x * 2 } | ⚖️ Nerozhodne |
| String Templates | "Hi %s".formatted(name) | "Hi $name" | 🏆 Kotlin |
| Extension Metódy | ✗ Nepodporované | ✓ Plne podporované | 🏆 Kotlin |
| Coroutines | Virtual Threads (Java 21+) | suspend fun + Flow | 🏆 Kotlin |
| Pattern Matching | switch výrazy (Java 21+) | when + sealed classes | ⚖️ Nerozhodne |
| Smart Casts | Manuálny cast po instanceof | Automatický po is | 🏆 Kotlin |
| Default Parametre | ✗ (použiť overloading) | ✓ Zabudované | 🏆 Kotlin |
Porovnanie výkonu & operácií
| Aspekt | Java | Kotlin | Poznámky |
|---|---|---|---|
| Runtime Výkon | Baseline | Rovnaký bytecode | Identický (oba používajú JVM) |
| Rýchlosť kompilácie | ✓ Rýchlejšia | Pomalšia (zlepšuje sa v 2.x) | Java ~40% rýchlejšia |
| Veľkosť buildu | Baseline | +~1MB stdlib | Zanedbateľné pre väčšinu aplikácií |
| Využitie pamäte | Baseline | Takmer identické | <2% rozdiel |
| Čas štartu | Baseline | Takmer identický | Rovnaký JVM warmup |
| Zrelosť nástrojov | 🏆 Vynikajúca (20+ rokov) | Veľmi dobrá (IntelliJ najlepší) | Java širšia IDE podpora |
| Veľkosť ekosystému | 🏆 Masívna | Veľká (všetky Java knižnice fungujú) | Java má viac knižníc |
Kedy vybrať čo
| Scenár | Odporúčanie | Dôvod |
|---|---|---|
| Greenfield projekt | 🏆 Kotlin | Moderné vlastnosti, null safety, menej boilerplate |
| Existujúca Java kódová základňa | ⚠️ Java (alebo postupný Kotlin) | Cena migrácie > benefity pokiaľ nie veľký refaktoring |
| Android development | 🏆 Kotlin | Oficiálne Google odporúčanie |
| Tímová expertíza | 🏆 Java ak tím je Java-only | Učebná krivka záleží |
| Vývoj knižníc | ⚖️ Oboje (mierna výhoda Java) | Java má lepšiu multi-jazykovú kompatibilitu |
| Kritická rýchlosť štartu | ⚖️ Nerozhodne | Oba kompilujú do rovnakého bytecode |
| Kritická rýchlosť buildu | 🏆 Java | Rýchlejšia kompilácia, záleží pre veľké kódové základne |
| Low-latency systémy | ⚖️ Nerozhodne | Identický výkon po JIT |
Citovať túto príručku
Ak odkazujete na túto príručku, prosím odkazujte na pôvodnú URL a uveďte autora.