Programmiersprache Kotlin – Nicht nur im Android-Umfeld interessant

Kotlin ist eine relativ neue, statisch typisierte, JVM-Programmiersprache, die von JetBrains [1] entwickelt wird. Die Version 1.0 wurde Anfang 2016 veröffentlicht. Trotzdem ist Kotlin bereits seit längerem verfügbar und wurde auch vor diesem Release produktiv eingesetzt. Vor allem in der Android-Community hat Kotlin einige Anhänger gewinnen können und ist spätestens seit der diesjährigen Google Konferenz „I/O“ in aller Munde, als man erklärte, dass Kotlin nun „offizielle Sprache für Android“ ist. Wir bei der Firma n-design sind ständig gespannt, wenn es um neue Frameworks und Programmiersprachen geht, sodass auch diese Entwicklung mit Interesse verfolgt wird.

Warum ist Kotlin so interessant für Android?

Der Grund ist relativ einfach, denn Kotlin kann zu Java 1.6 Bytecode kompiliert werden. Android unterstützt in den meisten Versionen nicht das neuste JDK 1.8, wodurch Android-Entwickler auf moderne Java-Sprachfeatures wie Lambdas und Streams [2] verzichten müssen. Kotlin jedoch bietet diese Features, und viele weitere, natürlich ebenfalls an, obwohl lediglich Java 6 benötigt wird, um den kompilierten Code auszuführen. JetBrains‘ Idee beim Entwickeln der Sprache war, eine Alternative zu Java zu schaffen, die Probleme behebt, die Produktivität steigert und weiterhin mit Java-Code vollständig interagieren kann. Ich denke, dass jeder Java-Entwickler, der bereits mit JVM-Sprachen wie Groovy und Scala gearbeitet hat, weiß, dass Java oft sehr „geschwätzig“ ist und viel Boilerplate-Code geschrieben werden muss, auch um simple Dinge zu bewerkstelligen. Kotlin setzt hier an und ermöglicht das Schreiben von deutlich kürzerem Sourcecode, wobei Scala erkennbar als Inspiration diente.

Kotlin-Syntax

Ich möchte im Folgenden einige Beispiele zeigen, die demonstrieren sollen, wie Kotlin-Sourcecode sich anfühlt:

Type Inference lokaler Variablen

Die folgenden Zeilen zeigen, wie Variablen in Kotlin deklariert werden:

var myvar1 = 100
var myvar2 = "text"
Die Typen von myvar1 und myvar2 (Int bzw. String) werden vom Compiler automatisch bestimmt, sodass sie nicht explizit angegeben werden müssen.

TIP: Den beiden Variablen kann beliebig oft ein anderer Wert zugeordnet werden. Möchte man final Variablen verwenden, so nutzt man val anstelle von var. Die einzelnen Zeilen müssen nicht mit ; terminiert weren.

Data Class

Wenn man sich überlegt, wie man ein POJO Person in Java entwickeln würde, das Properties wie name, age und job besitzt und natürlich angemessene Implementierungen für toString(), hashCode() und equals(), sowie die notwendigen Getter und Setter beinhaltet, so hat man direkt eine relativ große Datei vor Augen, die zudem einem sehr oft wiederkehrenden Prinzip entspricht.

Pojo in Kotlin
data class Person(val name: String, val age: Int, val job: String)
In Kotlin sieht das anders aus – Diese Zeile erzeugt all den gewünschten Code.

Null-Sicherheit

Wir alle fürchten NullpointerExceptions. Diese treten meistens aufgrund von Programmierfehlern auf, weil nicht klar war, dass bestimmte Werte zur Laufzeit null sein können.

Kotlin versucht diese Fehlerquelle zu eliminieren, indem zwischen jenen Variablentypen unterschieden wird, die nullable sind und solchen, die es nicht sind. Dies wird über die Syntax verdeutlicht. Wie man sich denken kann, sind Variablentypen die nicht null referenzieren können der Standard. Veranschaulicht bedeutet das, dass beispielsweise eine Variable vom Typ String niemals null enthalten kann. Der entsprechende Typ eines „nullable String“ wird als String? deklariert.

Dies alleine würde keinen allzu großen Unterschied machen, weshalb man bei der Verwendung von nullable Typen einige Besonderheiten beachten muss:

Beispiel: Arbeiten mit nullable String
val iCanBeNull:String? = null
//...do some hard work with the string...
iCanBeNull?.length

Die letzte Codezeile wird die Länge des Strings ergeben, wenn iCanBeNull nicht null referenziert, oder null im anderen Fall. Das heißt, der Aufruf length wird nicht auf einem Null-Pointer operieren und somit sind NullpointerExceptions unmöglich.

Man muss erwähnen, dass die Sprache durchaus die Möglichkeit offen lässt, solche Aufrufe zu erzwingen und NullpointerExceptions in Kauf zu nehmen. Aber sollte man das wirklich tun?!

String-Templates

Ein Feature, das jeder schnell versteht und man sich fragt, warum Java es nicht anbietet:

Verwende Variablen und Expressions direkt in Strings ohne explizite Konkatenation
var insertMe = "Reader"
println("Hello $insertMe") //Prints >>Hello Reader<< on Console
println("Hello ${insertMe.toUpperCase()}") //Prints >>Hello READER<< on Console

Methoden: Default-Wert für Parameter und benannte Argumente

In Java ist es oft notwendig eine Methode mehrfach zu überladen, um verschiedene API-Varianten bereitzustellen, bspw. wenn einer der Parameter nicht zwingend angegeben werden muss. Die Methode existiert dann mit und ohne diesen Parameter. Eine deutlich lesbarere Alternative ist in Kotlin möglich:

Default-Werte
fun myMethod(notOptional: String, optional: String = "defaultValForOptionalString"){
    println("myMethod called with $notOptional and $optional")
}

//Possible Calls
myMethod("arg1", "arg2")
myMethod("arg1")
Wie man sieht, wird für den zweiten Paramter ein Default-Wert definiert, sodass ein Aufruf mit lediglich dem ersten Parameter valide ist. Außerdem ist es möglich, beim Aufruf einer Methode die Namen der Parameter an die Argumente zu schreiben:
Benannte Argumente
//You choose your own arg sequence
myMethod(notOptional = "arg1", optional = "arg2")
myMethod(optional = "arg2", notOptional = "arg1")

Lambdas

Kotlin besitzt, anders als Java, angemessene Funktionstypen. Seit Java 1.8 ist es, wie wir wissen, möglich mit Lambdas und Streams ansatzweise funktional programmieren zu können. Allerdings muss man hierbei bedenken, dass die Umsetzung dessen ein wenig ungewöhnlich ist, wenn man mit funktionalen Programmiersprachen vertraut ist. Das Problem ist, dass Java keinen Funktionstypen kennt; Funktionen sind kein First Class Citizen innerhalb der Sprache. Stattdessen ist ein Lambda eine Implementierung eines SAM-Typen [3].

In Kotlin wird einer Funktion ein höherer Stellenwert zugeordnet. Es gibt einen Funktionstypen, der beispielweise so aussieht: (Int, Int) → Int (Eine Methode mit zwei Int-Parametern, die ein Int liefert).

anonyme Methode als Lambda
val multiply = { x: Int, y: Int -> x * y } //Typ: (Int, Int) -> Int
val res = multiply(3,5)

Eine Methode kann einfach einer Variablen zugeordnet und bei nächster Gelegenheit genutzt werden. Natürlich können Methoden auch als Parameter übergeben werden. Als syntaktisches Highlight hat man sich hier überlegt, dass es

  1. nicht notwendig ist runde Klammern anzugeben, falls ein Lambda das einzige Argument der Methode ist.
  2. nicht notwendig ist, den Methoden-Parameter zu benennen, falls er der einzige Parameter ist. Der implizite Name ist dann „it“.
  3. nicht notwendig ist, Methoden-Parameter überhaupt zu nutzen. Diese können mit „_“ benannt werden.

Diese Fälle sind nachfolgend dargestellt:

Lambda Syntaktischer Zucker
val ints = 1..4
//a) & b) filter() has only one parameter of type Int -> Boolean
ints.filter { it > 0 }
val map = mapOf(Pair(1,"val1"),Pair(2, "val2"))
//c) Key of Map-Entry is not used!
map.forEach { _, value -> println("$value!") }
Abschließend ein Beispiel, dass die ganze Schönheit und, wie ich finde, Lesbarkeit demonstriert:
Lambda-Beispiel
//Determine Names of all Males in people-collection
val namesOfMaleMembers = people.filter { it.gender == Person.Sex.MALE }
                                .map { it.name }

Weiteres

Die Beispiele sind nur ein kleiner Ausschnitt der vielen tollen Features, die Kotlin bietet und man sich zu gerne in Java wünschen würde. Ich habe bereits einige Zeit mit Kotlin arbeiten können und bin ein großer Fan und Unterstützer geworden. Der Einstieg in die Sprache kann sehr schnell gelingen, anders als es bei mir persönlich bei Scala der Fall war.

TIP: Einige der oben vorgestellten Sprachkonstrukte sind in der langen Liste der „JDK Enhancement Proposals“ (JPE) ebenfalls in ähnlicher Weise gewünscht, z.B. beschreibt JEP 286: Local-Variable Type Inference, beschrieben von Brian Goetz, die Typinferenz lokaler Variablen. Vielleicht ein Teil von Java 10?

Gibt es bedenkenswerte Features in Kotlin?

Ich denke, diese Frage kann man prinzipiell mit Nein beantworten. Allerdings erlaubt Kotlin gewisse Freiheiten, die mit Bedacht verwendet werden sollten. Es ist beispielsweise so, dass Kotlin es erlaubt, Funktionen und Variablen in Dateien zu definieren, die keiner Klasse zugeordnet sind, wie es in Java immer der Fall wäre. Diese können unqualifiziert an beliebiger Stelle referenziert werden [4]. Ein sinnvoller Anwendungsbereich sind statische Utility-Methoden (z.B. Collections). Die Frage ist: Wer darf, an welcher Stelle, solche Dinge innerhalb des Projekts definieren? Man sollte sich meiner Meinung nach projektintern absprechen, wie mit solchen Dingen umzugehen ist und bestenfalls eine zentrale Stelle definieren.

Ähnliches gilt für „Extension-Functions“: Dies sind im Prinzip Methoden, die einer Klasse von außen hinzugefügt werden. Beispielsweise könnte man die Klasse String mit beliebigen Methoden erweitern, obwohl sie eigentlich final ist und nicht per Vererbung erweiterbar wäre [5]. Genau genommen ist dies der absolut richtige Ansatz, ganz nach dem Open/Closed-Prinzip, welches hierdurch durchgesetzt wird. Für den Aufrufer ist leider nicht direkt ersichtlich, ob eine Member-Funktion oder eine künstliche Erweiterung aufgerufen wird. Allerdings gilt auch hier, dass ein Import notwendig wird, wodurch die Kritik etwas gemildert wird. In meinen Augen ist auch dies trotzdem ein mit Vorsicht zu nutzendes Feature. Ähnlich wie bei Top-Level Funktionen sollte klar festgelegt sein, wo solche Funktionen zu definieren sind.

Ist Kotlin eine ernstzunehmende Alternative zu Java?

Meiner Meinung nach ist Kotlin aktuell die JVM-Sprache, die es schaffen kann, Java ernsthaft Konkurrenz zu bieten. Kotlin ermöglicht das Erstellen von Applikationen für die JVM, Android, den Browser und bald sogar nativer Art. Jüngste Ereignisse im Zusammenhang mit Google und der Bekanntgabe, dass neben Java nun auch Kotlin offiziell zur Programmierung von Android-Apps herangezogen werden kann, hat das Potenzial einen Hype auszulösen und die Sprache damit bekannter zu machen. Bereits viele Unternehmen nutzen Kotlin produktiv, es entsteht eine Community und die erste Konferenz ist für dieses Jahr geplant. Anders als viele andere JVM-Sprachen hat Kotlin den grandiosen Vorteil, dass es zu 100% mit Java interoperabel ist. Konkret bedeutet das, dass die vielen verfügbaren Java-Libraries und Frameworks problemlos genutzt werden können und bestehende Codebasen nicht vollständig migriert werden müssen.

1. Bestens bekannt für ihre IDEs, allen voran IntelliJ IDEA.
3. Ein Interface mit genau einer abstrakten Methode.
4. Ein Import auf Package-Ebene ist trotzdem notwendig.
5. Anders als in Java sind Klassen in Kotlin standardmäßig final, entsprechend „Effective Java, Item 17: Design and document for inheritance or else prohibit it.“