Was ist Kotlin?

28.10.2022
Von 
Martin Heller schreibt als freier Autor für die Schwesterpublikation InfoWorld.
Kotlin bietet im Vergleich zu Java große Vorteile wenn es um JVM- oder Android-Entwicklung geht. Das sollten Sie über die Programmiersprache wissen.
Die quelloffene Programmiersprache Kotlin kann (unter anderem) JVM- und Android-Entwicklern zahlreiche Vorteile erschließen.
Die quelloffene Programmiersprache Kotlin kann (unter anderem) JVM- und Android-Entwicklern zahlreiche Vorteile erschließen.
Foto: Trismegist san - shutterstock.com

Kotlin ist eine allgemeine, freie, quelloffene, statisch typisierte, "pragmatische" Programmiersprache, die ursprünglich für die Java Virtual Machine (JVM) und Android entwickelt wurde und Merkmale der objektorientierten und funktionalen Entwicklung kombiniert. Der Fokus von Kotlin liegt dabei auf:

  • Interoperabilität,

  • Sicherheit,

  • Klarheit und

  • Tool-Unterstützung.

Kotlin wurde im Jahr 2010 bei JetBrains, dem Unternehmen hinter IntelliJ IDEA, entwickelt und steht seit 2012 Open Source zur Verfügung.

Das Kotlin-Projekt auf GitHub listet mehr als 770 Mitwirkende auf. Davon entfällt ein Großteil auf die Teams bei JetBrains, aber auch knapp 100 externe Kontributoren haben bereits zu Kotlin beigetragen. Das Unternehmen JetBrains verwendet Kotlin in vielen seiner Produkte, unter anderem in seinem Flaggschiff-Produkt, der integrierten Entwicklungsumgebung (IDE) IntelliJ IDEA.

Kotlin - das prägnantere Java

Untenstehender Screenshot zeigt die automatische Konvertierung eines Java-Code-Beispiels (links) in Kotlin. Dabei fällt in erster Linie auf, dass die sinnlosen Wiederholungen, die mit der Instanziierung von Java-Variablen verbunden sind, entfallen.

Auf den ersten Blick wirkt Kotlin wie eine prägnantere und schlankere Version von Java.
Auf den ersten Blick wirkt Kotlin wie eine prägnantere und schlankere Version von Java.

Das Java Idiom:

StringBuilder sb = new StringBuilder();

wird in Kotlin zu:

val sb = StringBuilder()

Zudem können Sie dem Screenshot entnehmen, dass Funktionen mit dem Keyword fun definiert werden und Semikolons nun optional sind, wenn Zeilenumbrüche vorhanden sind. Das Schlüsselwort val deklariert eine schreibgeschützte Eigenschaft oder eine lokale Variable. Ähnlich deklariert var eine veränderbare Eigenschaft oder lokale Variable.

Dennoch ist Kotlin stark typisiert: val und var können nur verwendet werden, wenn der Typ abgeleitet werden kann. Andernfalls muss dieser deklariert werden. In Sachen Typinferenz scheint sich Kotlin mit jeder neuen Version zu verbessern.

Wenn Sie einen Blick auf die Funktionsdeklaration in beiden Fenstern des Screenshots werfen, sehen Sie, dass der Return Type in Java vor dem Prototype steht. In Kotlin folgt er allerdings auf den Prototype und wird - wie in Pascal - durch einen Doppelpunkt abgegrenzt. Auch wenn es aus dem gezeigten Beispiel nicht ganz ersichtlich wird: Kotlin hat die Java-Anforderung aufgeweicht, dass Funktionen Class Members sein müssen. In Kotlin können Funktionen

  • auf oberster Ebene in einer Datei,

  • lokal innerhalb anderer Funktionen,

  • als Mitgliedsfunktion innerhalb einer Klasse oder eines Objekts und

  • als Erweiterungsfunktion
    deklariert werden.

Erweiterungsfunktionen bieten eine C#-ähnliche Möglichkeit, eine Klasse um neue Funktionen zu erweitern - ohne ein Design Pattern wie Decorator zu verwenden.

Kotlin implementiert auch Builder, die sogar hinsichtlich ihres Types überprüft werden können. Zudem unterstützt die Programmiersprache "Delegated Properties", die wiederum genutzt werden können, um "Lazy Properties", "Observable Properties", "Vetoable Properties" und "Mapped Properties" zu implementieren. Darüber hinaus können viele asynchrone Mechanismen, die in anderen Sprachen verfügbar sind, als Bibliotheken über Kotlin-Coroutinen implementiert werden. Dazu gehören:

  • async/await aus C# und ECMAScript,

  • channels und select aus Go sowie

  • generators/yield aus C# und Python.

Kotlin - Funktionale Programmierung

Top-Level-Funktionen zuzulassen, ist nur der erste Schritt bei der funktionalen Programmierarbeit mit Kotlin. Die Sprache unterstützt zudem:

  • Funktionen höherer Ordnung,

  • anonyme Funktionen,

  • Lambdas,

  • Inline-Funktionen,

  • Closures,

  • Tail-Rekursion und

  • Generics.

Mit anderen Worten: Kotlin bringt alle Eigenschaften und Vorteile einer funktionalen Programmiersprache mit. Im Folgenden betrachten wir einige funktionale Kotlin-Idiome.

Listen filtern mit Kotlin

val positives = list.filter { x -> x > 0 }

Das lässt sich durch die Nutzung von it noch weiter verkürzen, wenn nur ein einziger Parameter in der Lambda-Funktion vorhanden ist:

val positives = list.filter { it > 0 }

Maps/Listen von Paaren in Kotlin durchlaufen

for ((k, v) in map) { println("$k -> $v") }

k und v können dabei beliebig benannt werden.

Ranges mit Kotlin verwenden

for (i in 1..100) { ... } // closed range: includes 100

for (i in 1 until 100) { ... } // half-open range: does not include 100

for (x in 2..10 step 2) { ... }

for (x in 10 downTo 1) { ... }

if (x in 1..10) { ... }

Diese Beispiele demonstrieren sowohl das Keyword for, als auch die Verwendung von Ranges.

Obwohl es sich bei Kotlin um eine vollwertige, funktionale Programmiersprache handelt, bleibt der objektorientierte Charakter von Java als alternativer Programmierstil weitgehend erhalten. Das ist äußerst praktisch, wenn es darum geht, bestehenden Java-Code zu konvertieren. Wie Java 8 nutzt auch Kotlin Klassen mit Konstruktoren, sowie verschachtelte und anonyme innere Klassen, auch die Benutzerobeflächen sind ähnlich gestaltet. In Kotlin gibt es allerdings das Keyword new nicht. Um eine Klasseninstanz zu erstellen, rufen Sie den Konstruktor wie eine normale Funktion auf (siehe Screenshot).

Alle Kotlin-Klassen haben eine Standard-Superklasse Any, die nicht mit der Java-Basisklasse java.lang.Object identisch ist. Any enthält nur drei vordefinierte Mitgliedsfunktionen:

  • equals(),

  • hashCode() und

  • toString().

Kotlin-Klassen müssen mit dem Keyword open gekennzeichnet werden, damit andere Klassen von ihnen "erben" können. Bei Java-Klassen ist das Gegenteil der Fall: Sie sind "vererbbar", solange sie nicht mit dem Keyword final versehen sind. Um die Methode einer Superklasse zu überschreiben, muss diese selbst mit open und die Subklassen-Methode mit override gekennzeichnet sein. Diese Methode steht im Einklang mit der Kotlin-Philosophie, Dinge explizit zu benennen, statt sich auf Standardwerte zu verlassen und kann dabei unterstützen, häufige Java-Fehler zu vermeiden.

Kotlin - Sicherheitsfunktionen

Apropos Fehlervermeidung: Kotlin wurde auch entwickelt, um die Gefahr von Null Pointer References zu minimieren und den Umgang mit Null-Werten zu optimieren. Das wird erreicht, indem:

  • null für Standardtypen nicht zulässig ist,

  • nullable-Typen hinzugefügt werden und

  • Abkürzungsnotationen implementiert werden, um null-Tests zu behandeln.

Eine reguläre Variable vom Typ String kann zum Beispiel keine null-Werte enthalten:

var a: String = "abc"

a = null // compilation error

Wenn Sie Nullen zulassen müssen, etwa für SQL-Abfrageergebnisse, können Sie einen Nullable Type deklarieren, indem Sie ein Fragezeichen an den Typ anhängen:

var b: String? ="abc"

b = null // ok

Die Schutzmaßnahmen gehen jedoch noch ein wenig weiter: Sie können einen Non-Nullable Type ungestraft verwenden, müssen jedoch einen Nullable Type auf Nullwerte testen, bevor Sie ihn verwenden. Um die normalerweise im Rahmen von Nulltests nötigen, komplexen Eingaben zu vermeiden, hat Kotlin einen sogenannten "Safe Call" an Bord: ?..

Diese Syntax lässt sich gut verketten und macht eine Menge umständlicher Logik überflüssig, insbesondere, wenn ein Objekt aus einer Reihe von Datenbankabfragen erstellt wird, von denen jede fehlschlagen kann. Zum Beispiel würde bob?.department?.head?.name den Namen von Bobs Abteilungsleiter zurückgeben, wenn Bob, die Abteilung und der Abteilungsleiter alle ungleich Null sind.

Um eine bestimmte Operation nur für Nicht-Null-Werte durchzuführen, können Sie den Safe Call Operator in Kombination mit let verwenden:

val listWithNulls: List<String?> = listOf("A", null)

for (item in listWithNulls) {

item?.let { println(it) } // prints A and ignores null }

Oft möchte man einen gültigen, aber speziellen Wert aus einem nullbaren Ausdruck zurückgeben, normalerweise, um ihn in einem nicht-nullbaren Typ zu speichern. Dafür gibt es eine spezielle Syntax - den Elvis-Operator (ja, wirklich): ?:.

val l = b?.length ?: -1

ist das Äquivalent zu:

val l: Int = if (b != null) b.length else -1

In gleicher Weise verzichtet Kotlin auf die "Checked Exceptions" von Java, bei denen es sich um auslösbare Bedingungen handelt, die abgefangen werden müssen.

Appendable append(CharSequence csq) throws IOException;

Diese beispielhafte JDK-Signatur erfordert, eine IOException abzufangen - jedes Mal, wenn Sie Append-Methode aufrufen:

try {

log.append(message)

}

catch (IOException e) {

// Do something with the exception

}

Kotlin - Coroutines

Coroutines in Kotlin sind im Wesentlichen leichtgewichtige Threads. Man startet sie mit dem Coroutine-Builder launch im Kontext eines CoroutineScope. Einer der nützlichsten Coroutine-Scopes ist runBlocking{}, der sich auf den Bereich des Codeblocks bezieht.

import kotlinx.coroutines.*

fun main() = runBlocking { // this: CoroutineScope

launch { // launch a new coroutine in the scope of runBlocking

delay(1000L) // non-blocking delay for 1 second

println("World!")

}

println("Hello,")

}

Dieser Code erzeugt folgenden Output mit einer Verzögerung von einer Sekunde zwischen den Zeilen:

Hello,

World!

Kotlin - Android-Entwicklung

Bis zum Mai 2017 waren Java und C++ die einzigen offiziell von Android unterstützten Programmiersprachen. Dann kündigte Google 2017 an, Kotlin auf Android offiziell zu unterstützen. Ab Android Studio 3.0 ist Kotlin in das Entwicklungs-Toolset integriert. Frühere Versionen von Android Studio lassen sich über ein Plugin mit Kotlin ausstatten.

Kotlin lässt sich in denselben Bytecode kompilieren wie Java, interagiert auf natürliche Weise mit Java-Klassen und nutzt die gleichen Werkzeuge wie Java. Da es keinen Overhead für das Hin- und Herwechseln zwischen Kotlin und Java gibt, ist es absolut sinnvoll, Kotlin schrittweise einer Android-App hinzuzufügen, die in Java läuft. Die wenigen Fälle, in denen die Interoperabilität zwischen Kotlin- und Java-Code zu wünschen übriglässt (etwa wenn es um "Set-Only Properties" geht), treten nur selten auf und sind in der Regel leicht zu beheben.

Zu den Aushängeschildern für Android-Apps auf Kotlin-Basis gehören beispielsweise:

  • Pinterest,

  • Evernote,

  • Trello,

  • Gradle,

  • Corda,

  • Spring und

  • Coursera.

Kotlin vs. Java

Die Frage, ob Kotlin oder Java für die Entwicklung neuer Apps die bessere Wahl ist, stellt sich innerhalb der Android Community schon seit der Veröffentlichung von Kotlin 1.0 im Jahr 2016. Um es kurz zu machen: Kotlin-Code ist sicherer und prägnanter als sein Java-Äquivalent. Zudem können Kotlin- und Java-Daten in Android-Apps koexistieren, womit die Programmiersprache von JetBrain nicht nur für neue Applikationen nützlich ist, sondern auch, wenn bestehende Java-Apps erweitert werden sollen.

Einzig für absolute Android-Neulinge könnte das Hürden aufwerfen, da die meisten Android-Dokumentationen und -Beispiele historisch gesehen in Java verfasst sind. Andererseits ist die Konvertierung von Java nach Kotlin in Android Studio eine einfache Copy-Paste-Angelegenheit.

Für so gut wie jeden Android-Entwickler sind die Vorteile von Kotlin überzeugend. Der typische Zeitaufwand für einen Java-Entwickler, um Kotlin zu erlernen, beträgt ein paar Stunden - was angesichts der genannten Vorteile überschaubare Kosten aufwirft. Grob geschätzt, lässt sich die Anzahl der Codezeilen in Java durch eine Konvertierung in Kotlin um circa 40 Prozent reduzieren.

Kotlin vs. Scala

Zwischen Kotlin und Scala entscheiden sich hingegen nur wenige Entwicker innerhalb der Android-Community. Auf GitHub (Stand: Oktober 2022) finden Sie etwa 50.000 Android-Repositories, die Java verwenden, 24.000, die Kotlin nutzen, und ganze 73, die Scala verwenden. Android-Apps in Scala zu schreiben, ist zwar möglich, aber nur wenige Developer laden sich diese Mühe auf. Abseits von Android sieht die Sache allerdings ein wenig anders aus: Apache Spark zum Beispiel ist größtenteils in Scala geschrieben - ebenso wie viele Big-Data-Anwendungen für Spark.

In vielerlei Hinsicht stellt auch Scala eine Verschmelzung der objektorientierten und funktionalen Programmierarbeit dar. Entsprechend teilen sich Scala und Kotlin viele Konzepte und Notationen, beispielsweise, wenn es um unveränderliche Deklarationen mit val und veränderliche mit var geht. In anderen Punkten unterscheiden sich die Programmiersprachen marginal (einfacher Pfeil vs. Doppelpfeil, etc.). Kotlin definiert nullbare Variablen auf eine Art und Weise, die der von Groovy, C# und F# ähnlich ist. Scala hingegen definiert nullable Variablen mit Hilfe von Option.

Ein klarer Makel von Scala: Die Kompilierzeiten können sehr lang ausfallen. Kotlin hingegen wurde darauf konzipiert, in den häufigsten Softwareentwicklungsszenarien schnell kompiliert zu werden und schlägt in diesem Bereich auch Java.

Kotlin - Interoperabilität mit Java

An dieser Stelle fragen Sie sich vielleicht, wie Kotlin angesichts der genannten Unterschiede zu Java die Ergebnisse von Java-Interoperabilitätsaufrufen behandelt. Kotlin leitet stillschweigend und zuverlässig einen so genannten "Platform Type" her, der sich genau wie ein Java-Typ verhält - er ist also nullable, kann aber auch Null-Pointer-Ausnahmen erzeugen. Es gibt keine explizite Sprachnotation für einen Plattformtyp, aber für den Fall, dass Kotlin einen Plattformtyp angeben muss, wird ! an den Typ angefügt.

In den meisten Fällen funktioniert der Aufruf von Java-Code aus Kotlin so, wie man es erwarten würde. Wenn zum Beispiel in einer Java-Klasse sowohl Getter als auch Setter vorhanden sind, behandelt Kotlin sie als Eigenschaften mit demselben Namen. Ähnlich werden boolesche Accessor-Methoden als Eigenschaften behandelt, die den gleichen Namen wie die Getter-Methode haben. Ein Beispiel:

import java.util.Calendar

fun calendarDemo() {

val calendar = Calendar.getInstance()

if (calendar.firstDayOfWeek == Calendar.SUNDAY) { // call getFirstDayOfWeek()

calendar.firstDayOfWeek = Calendar.MONDAY // call setFirstDayOfWeek()

}

if (!calendar.isLenient) { // call isLenient()

}

}

Dieses Schema versagt im Fall von set-only properties in Java, weil sie von Kotlin (noch) nicht unterstützt werden. Wenn eine Java-Klasse nur einen Setter hat, ist sie in Kotlin nicht als Eigenschaft sichtbar.

Die Interoperabilität von Kotlin mit Java erstreckt sich auch auf Java-Tools: Kotlin hat keine eigenen Editoren oder IDEs, sondern verfügt über Plug-ins für die gängigen Java-Editoren und IDEs, darunter IntelliJ IDEA, Android Studio und Eclipse. Kotlin hat darüber hinaus kein eigenes Build-System, sondern verwendet Gradle, Maven und Ant.

Kotlin - Interoperabilität mit JavaScript

Sie können Kotlin auch verwenden, um Code für Browser und Node.js zu schreiben. In diesem Zusammenhang wird Kotlin in JavaScript ES5.1 transpiliert, anstatt in JVM-Bytecode kompiliert zu werden. Da JavaScript eine dynamische Sprache ist, fügt Kotlin einen dynamischen Typ hinzu, der in Kotlin für die JVM nicht verfügbar ist:

val dyn: dynamic = ...

Der Kotlin-Type-Checker ignoriert dynamic-Typen und überlässt es JavaScript, mit ihnen zur Laufzeit umzugehen. Ausdrücke, die Werte des dynamic-Type verwenden, werden wie geschrieben in JavaScript übersetzt. Auch hier überlässt Kotlin JavaScript die Interpretation der Ausdrücke zur Laufzeit.

Es gibt zwei Möglichkeiten, JavaScript von Kotlin aus aufzurufen:

  • über dynamic-Types oder

  • stark typisierte Kotlin-Header für JavaScript-Bibliotheken.

Um auf bekannte JavaScript-Frameworks von Drittanbietern mit einer stark typisierten API zuzugreifen, können Sie TypeScript-Definitionen aus dem DefinitelyTyped-Repository mit dem ts2kt-Tool nach Kotlin konvertieren.

Mit der Funktion js("...") können Sie JavaScript-Code als Text in Ihren Kotlin-Code einbinden. Paketdeklarationen in Kotlin-Code lassen sich mit dem external-Modifikator als reines JavaScript markieren. Sie können Kotlin-Code von JavaScript aus aufrufen, müssen dafür aber voll qualifizierte Namen verwenden.

Kotlin - Applikationen

Kotlin kann für jede Art von Entwicklungsarbeit verwendet werden, von Multiplattform-Projekten über server- und clientseitige Webentwicklung bis hin zu Data Science.

Laut dem "Kotlin Census 2020", der aktuellen Umfrage von JetBrains innerhalb der Kotlin-Developer-Community, sind die beliebtesten Development-Einsatzgebiete von Kotlin:

  • Mobile (63 Prozent),

  • Web Backend (40 Prozent),

  • Bibliotheken oder Frameworks (17 Prozent),

  • Tooling (9 Prozent),

  • Desktop (5 Prozent),

  • Web Frontend (5 Prozent) und

  • Systeme (5 Prozent).

Im Vergleich zu Java ist Kotlin sicherer, prägnanter und expliziter. Wenn Sie bereits Java-Kenntnisse vorweisen können, werden Sie mit Kotlin im Handumdrehen produktive Ergebnisse erzielen. (fm)

Dieser Beitrag basiert auf einem Artikel unserer US-Schwesterpublikation InfoWorld.