Blog Programmiersprache Kotlin - Nicht nur im Android-Umfeld interessant

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."