Kotlin vs. Java: Unterschiede, Stärken und Einsatzgebiete im Vergleich

Null-Sicherheit, Records, Coroutinen und virtuelle Threads: Wie sich die beiden JVM-Sprachen heute unterscheiden
Aktualisiert:
Abstract
- #Kotlin
- #Java
- #JVM
- #Android
- #Null-Sicherheit
- #Coroutinen
- #Spring Boot
- #Programmierung
Java hat aufgeholt, Kotlin ist erwachsen geworden. Welche Sprache passt zu Ihrem Projekt und Ihrem Team?
Als dieser Beitrag 2021 zum ersten Mal erschien, war die Rollenverteilung recht klar: Java galt als solide, aber geschwätzig, Kotlin als die moderne, knappe Alternative für dieselbe Plattform. Inzwischen hat sich das Bild verschoben. Java erscheint alle sechs Monate in einer neuen Version und hat sich Records, Pattern Matching und virtuelle Threads zugelegt. Kotlin wiederum hat mit Version 2.0 einen komplett neuen Compiler bekommen und zielt mit Kotlin Multiplatform weit über Android hinaus.
Die Frage „Kotlin oder Java?“ ist deshalb keine Glaubensfrage mehr, sondern eine nüchterne Abwägung. Beide Sprachen laufen auf der Java Virtual Machine (JVM), teilen sich Bibliotheken und Werkzeuge und lassen sich im selben Projekt mischen. Entscheidend sind die Unterschiede im Detail, das Einsatzgebiet und das Team, das den Code langfristig pflegt.
Zwei Sprachen, eine Plattform
Java erschien 1996 und gehört heute Oracle, wird aber als Open-Source-Projekt OpenJDK von vielen Firmen gemeinsam weiterentwickelt. Alle zwei Jahre wird eine Version mit Langzeitunterstützung (LTS) ausgezeichnet, auf die sich Unternehmen typischerweise festlegen. Die jüngsten sind Java 21 aus dem Jahr 2023 und Java 25 aus dem Jahr 2025.
Kotlin stammt von JetBrains, dem Hersteller der Entwicklungsumgebung IntelliJ IDEA. Die Firma stellte die Sprache 2011 vor, die Version 1.0 folgte 2016. Benannt ist sie nach der Insel Kotlin vor St. Petersburg, eine Anspielung auf Java, das ebenfalls nach einer Insel heißt. JetBrains entwickelt die Sprache auch weiter. Die Kotlin Foundation, zu deren Gründungsmitgliedern JetBrains und Google gehören, wacht über inkompatible Änderungen an der Sprache und über die Marke.
Beide Sprachen kompilieren zu Bytecode, also zu einem Zwischenformat, das die JVM ausführt. Für den Betrieb macht es daher kaum einen Unterschied, ob ein Dienst in Java oder Kotlin geschrieben ist: Speicherverwaltung, Monitoring und Deployment funktionieren gleich. Kotlin kann zusätzlich zu nativem Maschinencode (Kotlin/Native), zu JavaScript und, derzeit noch im Beta-Stadium, zu WebAssembly übersetzt werden.
Interoperabilität: der wichtigste Grund für Kotlin
Kotlin wurde von Anfang an so entworfen, dass es Java-Code direkt aufrufen kann und umgekehrt. Eine Kotlin-Klasse kann eine Java-Klasse erben, Java-Bibliotheken wie Hibernate oder Jackson funktionieren ohne Umweg. Das ist der Hauptgrund, warum Teams Kotlin schrittweise einführen können: Neue Klassen entstehen in Kotlin, bestehende Java-Klassen bleiben, bis sie ohnehin überarbeitet werden. IntelliJ IDEA bringt sogar einen automatischen Konverter mit, dessen Ergebnis allerdings fast immer Nacharbeit braucht.
Ganz reibungslos ist das Zusammenspiel nicht. Java kennt keine Unterscheidung zwischen Typen, die null sein dürfen, und solchen, die es nicht dürfen. Werte aus Java-Code behandelt Kotlin deshalb als Plattformtypen, deren Null-Status unbekannt ist. Hier entsteht die Lücke, durch die auch in Kotlin-Projekten NullPointerExceptions schlüpfen. Annotationen wie die des JSpecify-Projekts helfen, diese Lücke zu schließen. Wie eine Migration in der Praxis abläuft und welche Stolperstellen es gibt, beschreibt unser Infothek-Artikel zur schrittweisen Migration von Java zu Kotlin.
Syntax im Vergleich: Wie viel kürzer ist Kotlin noch?
Der klassische Vorwurf an Java lautete: viel Text für wenig Inhalt. Ein Datenobjekt brauchte Konstruktor, Getter, equals, hashCode und toString. Kotlin löste das von Beginn an mit Datenklassen. Java zog 2021 mit Records nach. Der Unterschied ist seitdem deutlich kleiner:
// Kotlin: Datenklasse mit Standardwert und benannten Argumenten
data class Bestellung(
val nummer: String,
val betrag: Double,
val express: Boolean = false,
)
val bestellung = Bestellung(nummer = "B-1001", betrag = 49.90)
val eilig = bestellung.copy(express = true)
// Java 21+: Record mit derselben Struktur
public record Bestellung(String nummer, double betrag, boolean express) {
public Bestellung(String nummer, double betrag) {
this(nummer, betrag, false);
}
}
var bestellung = new Bestellung("B-1001", 49.90);
var eilig = new Bestellung(bestellung.nummer(), bestellung.betrag(), true);
Beide Varianten erzeugen unveränderliche Objekte mit sinnvollem Vergleich und lesbarer Ausgabe. Kotlin bleibt komfortabler, weil Standardwerte, benannte Argumente und die Methode copy eingebaut sind. In Java fehlen diese Funktionen bislang. Ein Ausdruck wie bestellung.copy(express = true) hat in Java noch keine direkte Entsprechung, an sogenannten Withers für Records wird aber gearbeitet.
Auch beim Einstieg ist Java schlanker geworden. Seit Java 25 braucht ein kleines Programm keine Klasse und kein public static void main(String[] args) mehr:
// Java 25: kompakte Quelldatei mit Instanz-main
void main() {
IO.println("Hallo aus Java");
}
// Kotlin: Funktion auf oberster Ebene
fun main() {
println("Hallo aus Kotlin")
}
Für Lehrbeispiele und Skripte ist der Abstand damit fast verschwunden. Im Alltag großer Anwendungen bleibt Kotlin knapper, vor allem durch Typinferenz an mehr Stellen, Ausdrücke statt Anweisungen (if und when liefern Werte) und Erweiterungsfunktionen. Letztere fügen bestehenden Klassen neue Methoden hinzu, ohne sie zu verändern:
// Erweiterungsfunktion: formatiert einen Betrag als Euro-Wert
fun Double.alsEuro(): String = "%.2f €".format(this)
println(49.9.alsEuro()) // Ausgabe je nach Gebietsschema: 49,90 € oder 49.90 €
Null-Sicherheit: Kotlins größter Vorsprung
Der vielleicht wichtigste Unterschied steckt im Typsystem. In Kotlin muss jeder Typ ausdrücklich angeben, ob er null enthalten darf. String ist immer ein Text, String? darf auch leer sein. Der Compiler verweigert die Übersetzung, wenn ein möglicherweise fehlender Wert ungeprüft verwendet wird:
data class Kunde(val name: String, val email: String?)
fun benachrichtige(kunde: Kunde) {
// Sicherer Aufruf mit ?. und Ersatzwert mit ?:
val adresse = kunde.email ?: return
println("Mail an $adresse")
// kunde.email.length würde nicht kompilieren, weil email null sein kann
val laenge = kunde.email?.length ?: 0
println("Länge der Adresse: $laenge")
}
Angenommen, ein Webshop importiert Kundendaten aus einem Altsystem, und bei einem Teil fehlt die E-Mail-Adresse. In Kotlin fällt das schon beim Kompilieren auf. In Java zeigt es sich häufig erst, wenn der Versand der Bestellbestätigung mit einer Exception abbricht. Java hat dafür Optional und Annotationen wie @Nullable, doch beides ist freiwillig und wird vom Standard-Compiler nicht erzwungen. Im OpenJDK-Projekt Valhalla wird an Typen gearbeitet, die null ausdrücklich ausschließen. Bis diese Teil der Sprache sind, bleibt Null-Sicherheit ein klarer Pluspunkt für Kotlin.
Moderne Sprachmittel: Java holt auf
Bei anderen Funktionen hat Java den Abstand in den letzten Jahren deutlich verringert. Sealed Classes, also Klassenhierarchien mit fest vorgegebenen Unterklassen, gibt es seit Java 17. Mit Pattern Matching in switch seit Java 21 lassen sich solche Hierarchien ähnlich elegant auswerten wie mit Kotlins when:
sealed interface Zahlung permits Karte, Rechnung, Paypal {}
record Karte(String nummer) implements Zahlung {}
record Rechnung(int zahlungszielTage) implements Zahlung {}
record Paypal(String konto) implements Zahlung {}
String beschreibe(Zahlung zahlung) {
return switch (zahlung) {
case Karte k -> "Kartenzahlung";
case Rechnung r -> "Rechnung, fällig in " + r.zahlungszielTage() + " Tagen";
case Paypal p -> "PayPal an " + p.konto();
};
}
sealed interface Zahlung
data class Karte(val nummer: String) : Zahlung
data class Rechnung(val zahlungszielTage: Int) : Zahlung
data class Paypal(val konto: String) : Zahlung
fun beschreibe(zahlung: Zahlung): String = when (zahlung) {
is Karte -> "Kartenzahlung"
is Rechnung -> "Rechnung, fällig in ${zahlung.zahlungszielTage} Tagen"
is Paypal -> "PayPal an ${zahlung.konto}"
}
In beiden Sprachen prüft der Compiler, ob alle Fälle abgedeckt sind. Kommt eine neue Zahlungsart hinzu, meldet er jede Stelle, die noch angepasst werden muss. Der Unterschied liegt nur noch in Details: Kotlin wandelt den Typ nach der Prüfung mit is automatisch um (Smart Cast), Java bindet ihn an eine neue Variable. Einen Überblick über weitere Neuerungen bietet unser Beitrag zu den Funktionen von Java 21.
Nebenläufigkeit: Coroutinen und virtuelle Threads
Ein Backend, das gleichzeitig Tausende Anfragen bedient, verbringt die meiste Zeit mit Warten, etwa auf die Datenbank oder einen externen Dienst. Klassische Threads des Betriebssystems sind dafür zu schwer: Jeder belegt Speicher, und ab einigen Tausend wird es eng.
Kotlin beantwortet das seit Jahren mit Coroutinen. Funktionen, die mit suspend markiert sind, können pausieren, ohne einen Thread zu blockieren. Die Bibliothek kotlinx.coroutines bietet dazu strukturierte Nebenläufigkeit: Gestartete Aufgaben gehören zu einem Bereich und werden gemeinsam abgebrochen, wenn etwas schiefgeht.
import kotlinx.coroutines.async
import kotlinx.coroutines.coroutineScope
suspend fun ladeKunde(id: Long): String = TODO("Datenbankabfrage")
suspend fun ladeBestellungen(id: Long): List<String> = TODO("REST-Aufruf")
// Beide Abfragen laufen parallel; scheitert eine, wird die andere abgebrochen
suspend fun uebersicht(id: Long): Pair<String, List<String>> = coroutineScope {
val kunde = async { ladeKunde(id) }
val bestellungen = async { ladeBestellungen(id) }
kunde.await() to bestellungen.await()
}
Java hat mit Version 21 einen anderen Weg gewählt: virtuelle Threads. Sie verhalten sich wie gewöhnliche Threads, werden aber von der JVM verwaltet und sind so leicht, dass eine Anwendung Hunderttausende davon starten kann. Der große Vorteil: Bestehender, blockierender Code profitiert ohne Umbau, ein eigenes Schlüsselwort wie suspend gibt es nicht. Strukturierte Nebenläufigkeit nach Kotlin-Vorbild ist in Java dagegen noch eine Vorschaufunktion. Mehr zu den Hintergründen steht im Beitrag über virtuelle Threads in Java.
Für Kotlin-Teams sind virtuelle Threads keine Konkurrenz, sondern eine Ergänzung, denn Kotlin auf der JVM kann sie ebenfalls nutzen. Coroutinen bleiben vor allem dort stark, wo es um feingranulare Steuerung geht, etwa bei Benutzeroberflächen auf Android oder bei Datenströmen mit Flow.
Einsatzgebiete: Android, Backend und Multiplatform
Android: Google erklärte Kotlin 2019 zur bevorzugten Sprache für Android. Die moderne Oberflächenbibliothek Jetpack Compose ist ausschließlich für Kotlin gedacht, neue Beispiele und Dokumentation erscheinen zuerst oder nur in Kotlin. Wer heute eine neue Android-App beginnt, hat kaum einen Grund, Java zu wählen.
Backend: Hier ist das Bild ausgeglichener. Java dominiert in Unternehmensanwendungen, nicht zuletzt wegen der großen Zahl erfahrener Entwickler:innen. Spring Boot unterstützt Kotlin aber vollwertig, und mit Ktor gibt es ein schlankes Kotlin-eigenes Framework. Bei bestehenden Spring-Anwendungen ist ein gemischter Betrieb verbreitet. Wer Spring-Projekte modernisiert, findet in unserer Themenseite zu Spring Boot Beratung und Seminare.
Multiplatform: Mit Kotlin Multiplatform lassen sich Geschäftslogik, Datenmodelle und Netzwerkcode zwischen Android, iOS, Desktop und Web teilen, während die Oberfläche nativ bleiben kann. Mit Compose Multiplatform lässt sich auch die Oberfläche gemeinsam schreiben. Java hat hier kein Gegenstück.
Werkzeuge: Build-Skripte für Gradle werden für neue Projekte standardmäßig in Kotlin geschrieben. Selbst reine Java-Teams kommen so oft mit Kotlin in Berührung. Ein bekannter Kritikpunkt sind die Übersetzungszeiten, die bei Kotlin länger waren als bei Java. Der mit Kotlin 2.0 eingeführte K2-Compiler arbeitet laut JetBrains deutlich schneller als sein Vorgänger.
Welche Sprache passt zu Ihrem Projekt?
Eine pauschale Antwort gibt es nicht, aber einige Faustregeln:
| Situation | Empfehlung |
|---|---|
| Neue Android-App | Kotlin |
| Code zwischen Android und iOS teilen | Kotlin Multiplatform |
| Große bestehende Java-Codebasis, stabiles Team | Java, neue Sprachfunktionen nutzen |
| Neues Spring-Backend, Team offen für Neues | Beides möglich, Kotlin bringt Null-Sicherheit |
| Viele Entwickler:innen mit Java-Erfahrung, hohe Fluktuation | Java |
Unsere Einschätzung: Die Zeiten, in denen Kotlin vor allem als Mittel gegen Javas Umständlichkeit galt, sind vorbei. Java ist heute eine moderne Sprache, und für Teams mit großer Java-Codebasis ist der Umstieg kein Selbstzweck. Kotlin lohnt sich dort, wo seine echten Stärken zählen: Null-Sicherheit, Coroutinen, Multiplatform und Android. Überschätzt wird dagegen die Ersparnis an Codezeilen, denn die eigentlichen Kosten in Projekten entstehen beim Verstehen und Pflegen, nicht beim Tippen.
Wichtiger als die Wahl selbst ist eine bewusste Entscheidung im Team. Zwei Sprachen im selben Projekt sind technisch problemlos, verlangen aber gemeinsame Konventionen, Code-Reviews durch Menschen, die beide beherrschen, und eine klare Linie, wo welche Sprache eingesetzt wird. Wenn Sie Kotlin einführen oder ein Team schulen möchten, finden Sie auf unserer Seite zu Kotlin-Beratung und Seminaren passende Angebote.
Häufige Fragen zu Kotlin und Java
Wie lange brauchen Java-Entwickler, um Kotlin zu lernen?
Wer Java sicher beherrscht, liest Kotlin-Code meist nach wenigen Tagen und schreibt nach einigen Wochen idiomatischen Code. Länger dauert es bei Coroutinen und funktionalen Mustern, die in klassischem Java-Code seltener vorkommen.
Ist Kotlin langsamer als Java?
Zur Laufzeit in der Regel nicht, weil beide zu vergleichbarem Bytecode kompilieren. Einzelne Sprachmittel wie Lambdas oder Standardwerte erzeugen etwas zusätzlichen Code, der in typischen Geschäftsanwendungen aber nicht ins Gewicht fällt. Spürbarer sind die Übersetzungszeiten, die sich mit dem K2-Compiler verbessert haben.
Muss eine bestehende Java-Anwendung komplett umgeschrieben werden?
Nein. Beide Sprachen können im selben Modul nebeneinander existieren. Üblich ist, neue Klassen und Tests in Kotlin zu schreiben und bestehenden Java-Code nur dann umzustellen, wenn er ohnehin geändert wird.
Hat Java noch eine Zukunft neben Kotlin?
Ja. Java wird mit festem Release-Rhythmus weiterentwickelt, ist in Unternehmen tief verankert und bekommt mit den OpenJDK-Projekten Valhalla, Loom und Amber weiterhin grundlegende Neuerungen. Kotlin profitiert davon, weil jede Verbesserung der JVM auch Kotlin-Anwendungen zugutekommt.
Kategorien
Schlagworte