Ist Kotlin das bessere Java?

Falls der geneigte Leser nicht weiss, was Kotlin ist: Kotlin ist eine neue Programmiersprache, welche quasi als Weiterentwicklung von Java entwickelt wurde.

Es ist bereits wieder einige Zeit vergangen, als Google auf der I/O Konferenz in 2017 Kotlin für Android offziell angekündigt hat. Und obwohl Kotlin bisher noch nicht so verbreitet ist, verwenden immer mehr Firmen die Programmiersprache für ihre Projekte – besonders für die Entwicklung von Android Apps. Ein weiteres Beispiel ist etwa Corda, die permissioned distributed ledger Blockchain.

Kotlin ist eine statically typed programming language, was soviel bedeuetet wie dass die Typen der Variablen zur Kompilierzeit geprüft werden statt zur Laufzeit des Programmes. So kann einem die IDE bereits bei der Programmierung auf Fehler hinweisen – Wie man es auch von Java kennt.

Kotlin wurde von JetBrains entwickelt, den gleichen Jungs, die auch die beliebte IntelliJ IDE produziert haben.

Kotlin hat gegenüber aber noch weitere Vorteile, die ich kurz auflisten werde.

Kompatibel zu Java

Das coole an Kotlin ist aber, dass Kotlin vollständig kompatibel mit Java ist. Kotlin läuft ebenfalls auf der JVM (Java Virtual Machine) wie Java und man kann ein Java Programm einfach in ein Kotlin Programm umwandeln (und zurück – für das Meiste, was ich hier schreibe, gelten beide Wege).

Man kann also auch seine alten Java Projekte nehmen und einfach mit Kotlin weitermachen. Man kann in einer Kotlin Datei zum Beispiel Java Klassen normal verwenden und instanzieren. Und das coole ist: alle bisher entwickelten Frameworks wie etwa Spring können ebenfalls mit Kotlin verwendet werden.

Fast identische Syntax

Wer lange Zeit als Java Entwickler gearbeitet hat und dann zum ersten Mal ein Kotlin Programm sieht wird erst mal stutzen. Es sieht doch alles ein wenig anders aus. Und macht das überhaupt Sinn? Hmm, also ich war da sehr skeptisch.

Grundsätzlich wurden aber viele Befehle vereinfacht. Man hat quasi überlegt, was man an Java verbessern könnte, und das dann in Kotlin umgesetzt.

Wenn man sich erst einmal daran gewöhnt hat, ist der Kotlin Code kleiner und einfacher als Java Code – und das finde ich immer eine gute Sache. Weniger Code bedeutet auch weniger Bugs 🙂

Dennoch arbeitet man am besten zuerst einmal mit Kotlin, um sich an die Syntax zu gewöhnen.

Viele kleine Verbesserungen

Es sind ganz viele Kleinigkeiten, die in Kotlin anders gemacht werden als in Java. Die meisten Änderungen, die ich bisher gesehen habe, machen aber durchaus Sinn.

Beispiele:

  • Variablen können nun direkt in Strings verwendet werden und müssen nicht mit + konkateniert werden.
  • Intelligentere Casts: Den Typ eines Objekts kann man statt mit instanceof neu mit schlange is Tier prüfen. Wurde etwa erfolgreich festgestellt, dass ein Tier eine Schlange ist, etwa in einem IF Statement, weiss der Kompiler von diesem Punkt an, dass es sich im ganzen IF Statement um eine Schlange handeln muss.
  • Bei Methoden kann man den Parametern nun Standardwerte angeben.
  • Getters und Setters in Pojos sind optional.
  • switch case wurde durch when ersetzt – wobei die Grösse des when Codes viel kleiner ist als beim switch case.
  • Data Classes sind eine Neuerung, welche die POJO’s (Plain Old Java Objects) verbessern. Eine Data Class ist wie eine normale Java Klasse, der Kompiler bietet aber Standardimplementationen von Methoden wie equals(), hashcode() oder toString() an. So kann ein Java Datenobjekt in einer einzigen Zeile deklariert werden (Objektname plus die dazugehörigen Attribute).
  • Eine Instanz kann nun automatisch in Attribute aufgeteilt werden, so dass man mit den einzelnen Attributen weiterarbeiten kann. Ein Schlange Objekt kann so etwa in zwei Attribute Länge und Farbe aufgeteilt werden.
  • Natürlich unterstützt Kotlin auch bewährte Java Funktionalitäten wie etwa Lambdas.

Ich denke nicht, dass es sinnvoll ist, an diesem Punkt gross Codebeispiele zu zeigen, die gibt es schon auf diversen Internetseiten, etwa der offiziellen Kotlin Referenz.

Kotlin lernen

Wer Kotlin lernen möchte, dem sind die Tutorials von Telusko auf Youtube sehr ans Herz gelegt:

Wer das Video mag klickt lieber auf diesen Link mit der gesamten Playlist, es sind nämlich insgesamt 46 Videos.

Telusko versteht es, auf witzige und einfache Weise alle Spezialitäten der Sprache in kurzen Videos darzulegen. Ein echt sympatischer Typ. Und dadurch, dass jedes Thema ein eigenes Video hat, kann man einfach zu den Themen springen, die einen am meisten interessieren.

Ist Kotlin denn nun das bessere Java?

Kotlin wird in der Programmiercommunity sehr positiv aufgenommen. Ob das langfristig der Fall sein wird, müssen wir abwarten. Kotlin ist modern und fühlt sich wie eine echte Verbesserung von Java an.

Hat man sich erst einmal daran gewöhnt, fühlt sich der Code auch einfacher an als in Java, was oftmals eine Kritik an Java war.

Wer die Möglichkeit hat, sollte Kotlin unbedingt mal ausprobieren. Der Einstieg ist durch die Verträglichkeit mit Java denkbar einfach.

Eines ist klar: Falls sich langfristig herausstellen sollte, dass Kotlin das neue, bessere Java ist, ist es unglaublich einfach, seine Java Projekte in Kotlin weiterzuentwickeln. Man verwendet die gleichen JAR’s oder Java Klassen und macht einfach mit Kotlin Dateien weiter. Es ist nicht nötig, jahrelange Migrationsprojekte wie etwa von Cobol auf Java durchzuführen.

Asynchroner Aufruf eines REST Services mit Spring aus einer JavaFX Applikation

Heutzutage sind ja REST Services sehr beliebt. Normalerweise läuft dieser Aufruf synchron ab und der Thread wartet schön, bis die Antwort zurückkommt. Dies ist aber auch einfach asynchron mittels asyncRestTemplate möglich (Welches nur bis Spring 4 funktioniert und in Spring 5 von WebClient abgelöst wird, oje…) Somit bleibt der aktuelle Thread nicht blockiert, sondern kann weiterarbeiten und irgendwann trudelt dann die Antwort des REST Services ein.

Statt nach dem REST Call mit .get() auf die Antwort zu warten, gibt man ihm zwei Callbacks mit: Einen SuccessCallback und ein FailureCallback. Der SuccessCallback wird bei erfolgreichem Aufruf aktiviert und enthält das Ergebnis des Services. Der FailureCallback wird aufgerufen, wenn beim Aufruf des REST Services oder im Service selber eine Exception auftritt. Das Objekt, dass wir dabei für die Callbacks verwenden, ist ein ListenableFuture vom Package „org.springframework.util.concurrent“.

Macht man das ganze in einer JavaFX Applikation, muss man noch etwas bestimmtes berücksichtigen. Ich will jetzt hier aber nichts spoilern.

Aufruf des Rest Services

Der Aufruf des Services ist simpel. Wir sagen, welchen Mediatype wir verwenden, bauen uns ein AsyncRestTemplate und rufen die exchange Methode auf. In diesem Beispiel wird die Antwort ein Objekt des Typs User sein. Obwohl der REST Service JSON zurückliefert, kann uns das egal sein, da Spring die Antwort automatisch nach JSON serialisiert und in das Objekt deserialisiert. Natürlich ist es dabei von enormen Vorteil, wenn die Serverseite das gleiche Objekt/die gleiche Klasse verwendet (In diesem Beispiel die Klasse User).

String url = "http://localhost:8089/meineresturl/user/11");
HttpHeaders headers = new HttpHeaders();
headers.setContentType(MediaType.TEXT_PLAIN);
HttpEntity request = new HttpEntity<>("params", headers);
AsyncRestTemplate asyncRestTemplate = new AsyncRestTemplate();

ListenableFuture> response = asyncRestTemplate.exchange(url, HttpMethod.GET, request,
User.class);

response.addCallback(successCallback, ex -> onFailure(ex));

Beim addCallback fällt auf, dass SuccessCallback und FailureCallback verschieden hinzugefügt werden. Das kann beliebig gemacht werden. In diesem Beispiel wurde der SuccessCallback in einer anderen Klasse erstellt (nämlich im JavaFX ViewModel, das Zugriff auf alle GUI Objekte hat) und hier hinzugefügt. So landet das Ergebnis am Ende praktischerweise in der anderen Klasse.

FailureCallback direkt in einer Methode

Der einfachere Weg, mit dem Ergebnis bzw. in diesem Fall einem Fehler umzugehen, ist es, mittels eines Lambdas direkt auf eine Methode zuweisen. In dieser Methode kann das Ergebnis wie gewünscht behandelt werden.

private void onFailure(Throwable ex) {
LOGGER.error("Fehler beim Aufruf des Services: ", ex);
}

Direktes Hinzufügen des SuccessCallbacks plus Wechseln in JavaFX Thread

Damit der SuccessCallback hinzugefügt werden kann, muss er irgendwo definiert werden. Wie gesagt, das kann in irgendeiner Klasse geschehen und dann der Klasse, die für den oben stehenden REST Call zuständig ist, übergeben werden.

Der Body der Response entspricht dabei dem angegebenen Objekt User und die Werte werden automatisch gesetzt. User muss dabei ein POJO mit Gettern und Settern sein, damit die Werte von Spring gesetzt werden können.

Mit .getStatusCode() kann der HTTP Statuscode abgefragt werden. Die gesamte Liste findet man auch in dieser Klasse org.springframework.http.HttpStatus.

public SuccessCallback> createDurchfahrtenSuccessCallback() {
return response -> {
HttpStatus httpStatusCode = response.getStatusCode();
if (HttpStatus.NOT_FOUND.equals(httpStatusCode)) {
LOGGER.info("HTTP Status Code 404 erhalten - Ressource nicht verfuegbar");
} else if (HttpStatus.OK.equals(httpStatusCode)) {
// Wieder in den JavaFX Hauptthread gehen
Platform.runLater(() -> {
User user = response.getBody();
speichereUser(user);
});
} else {
LOGGER.info("HTTP Status Code " + httpStatusCode + " erhalten"); }
};
}

In der Mitte des folgenden Codestücks sieht man auch den Teil, den man bei einem Aufruf aus einer JavaFX Applikation berücksichtigen muss. Die Antwort des REST Services kommt asynchron und läuft dort in irgendeinem Thread, aber nicht im JavaFX Hauptthread (der Code kann übrigens gedebuggt werden, einfach Breakpoint im Callback setzen).

Damit nun die Verarbeitung und somit der Zugriff auf die GUI Elemente klappt, muss man mittels Platform.runlater() in den JavaFX Thread wechseln, bevor man das Ergebnis verwendet.

Simulieren des REST Services in einem jUnit Test mittels WireMock

Programmiert man gleichzeitig die Client- wie auch Serverseite eines REST Services kann es durchaus vorkommen, dass der Code für den Aufruf des Services vor dem eigentlichen Service-Code fertig ist.

In diesem Fall kann man mittels des Frameworks WireMock einfach einen REST Service für Testzwecke simulieren.

Dazu erstellt man wie gewohnt einen jUnit Test und verwendet WireMock mit den gewünschten Parametern. Natürlich muss das WireMock JAR vorher noch dazugeladen werden, etwa im Maven POM File:

groupId: com.github.tomakehurst
artifactId: wiremock
version: 2.19.0
scope: test

Als erstes erstellt man eine sogeannnte WireMockRule und gibt den gewünschten Port an (dieser muss dann natürlich in der URL beim REST Service Aufruf auch drin sein).

import com.github.tomakehurst.wiremock.junit.WireMockRule;

@Rule
public WireMockRule wireMockRule = new WireMockRule(8089);
// No-args constructor defaults to port 8080

Das eigentliche Simulieren des REST Services geschieht dann tatsächlich in nur einer einzigen Zeile Code, in der man sagt, welchen HTTP Statustyp man zurückgeben will (200 = Alles OK), ob es in JSON sein soll und welchen Body man senden will.

String url = "/meineresturl/user/11");  // URL ohne Host

stubFor(get(urlEqualTo(url)).willReturn(aResponse().withStatus(200)
.withHeader("Content-Type", "application/json")
.withBody(userJson)));

Der Body kann ein einfacher String „Hallo Welt“ sein. Ich habe in meinem Fall ein Test User erstellt, ein paar Beispielwerte reingepflanzt und dann das ganze Objekt mittels Jackson ObjectMapper von Hand serialisiert.

import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;

private String userToJson(User user) {
String userJson = "";
ObjectMapper mapper = new ObjectMapper();
try {
userJson = mapper.writeValueAsString(user);
} catch (JsonProcessingException e) {
e.printStackTrace();
}
return user;
}

Also zuerst definiert man im jUnit Test den REST Service mit stubFor und kann dann eine Instanz des REST Service Aufrufers verwenden, um den REST Service aufzurufen. Falls es zu Beginn nicht klappt muss man sicherstellen, dass die URL, die der Service Aufrufer verwendet, wirklich 1-zu-1 mit der URL übereinstimmt, die man dem stubFor mitgibt.

Als einfacher Test kann man also zum Beispiel einen User mit Namen „Peter“ erstellen, diesen in JSON Serialisieren und dort als Body angeben. Dann rufe ich den REST Service auf, erhalte automatisch das „User“ Objekt und prüfe mit jUnit

assertEquals("Peter", user.getName());

WireMock hat noch viele weitere Möglichkeiten, um das Resultat zu verifizieren. Siehe dazu http://wiremock.org/

PS: Muss man beim REST Service Aufruf wie ganz oben beschrieben den SuccessCallback mitgeben muss man im jUnit Test natürlich auch so einen definieren, damit man das Ergebnis bearbeiten kann. Oder man verwendet beim SuccessCallback einfach auch die direkte Methode wie beim FailureCallback.

Ich musste übrigens noch einen kleinen Sleep zwischen dem Aufruf des REST Services und dem anschliessenden assertEquals einbauen, damit der jUnit Test erfolgreich war.

private void warteAufCallback() {
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
}

So das wäre alles für heute. Ich hoffe, der Beitrag erleichtert jemandem das Leben 🙂

Was sind Java 8 Functional Interfaces?

Java 8 hat eine besonders interessante Funktionalität mitgebracht: Lambdas. Ein Lambda ist eine anonyme Funktion und kann etwa als Parameter einer Methode über- oder von einer Methode zurückgegeben werden.

Vor Java 8 hat man üblicherweise eine Klasse erstellt wenn man irgendein Stück Code verwenden wollte. Dies ist aber nicht immer nötig und generiert unnötigen Boilerplate Code.

Zu Lambdas komme ich aber ein anderes Mal. Jetzt geht es um die Functional Interfaces.

Kurze Übersicht

Functional Interfaces:

  1. Consumer = Akzeptiert ein Argument und gibt nichts zurück
  2. BiConsumer = Akzeptiert zwei Argumente, etwa von einer Hashmap, und gibt nichts zurück
  3. Supplier = Will kein Argument, aber gibt eines zurück
  4. Predicate = Gibt true oder false zurück
  5. Operator = Will den gleichen Typ als Input und Output

Wie sehen Functional Interfaces aus?

Ein Functional Interface hat üblicherweise eine @FunctionalInterface Annotation. Der Trick bei einem Functional Interface ist, dass diese eine einige abstrakte Methode besitzt. Die Implementation dieser Methode kann dann als Lambda geschehen.

Die neuen „Default“ Methoden, die auch mit Java 8 eingeführt wurden, sind übrigens nicht abstract und zählen daher auch nicht zu den Functional Interfaces.

Das Consumer Interface

Das Interface, was wohl am meisten verwendet wird, ist das Consumer Interface. Ein Consumer akzeptiert ein Argument und gibt nichts zurück. Mit dem Argument wird irgendetwas angestellt.

Zum Beispiel können wir eine Liste von Namen erstellen und dann jeden mit „Hallo“ begrüssen. Die Liste kann ganz einfach mit „Arrays.asList()“ gemacht werden (achtung: Das Arrays hat ein grosses ‚A‘).

List<String> namen = Arrays.asList("Hans", "Wurst", "Gerald");
namen.forEach(name -> System.out.println("Hallo " + name");

Bei der Methode forEach sehen wir schön, dass ein Consumer benötigt wird, welcher einen Parameter braucht, in diesem Fall einen String.

funcint1.PNG

Output:
Hallo Hans
Hallo Wurst
Hallo Gerald

Wenn wir die Klasse „java.util.function.Consumer“ öffnen sieht man am Anfang die Annotation @FunctionalInterface. Die abstrakte Methode ist in diesem Fall „accept“, da wir aber ein obercooles Lambda mit dem -> Pfeil verwendet haben, müssen wir diesen Namen gar nicht kennen.

funcint2.PNG

PS: Die Angabe zu „references | implementation“ ist übrigens vom Code Mining, das gehört nicht zu der Klasse, siehe Code Mining in Eclipse Photon

Das BiConsumer Interface

Ein BiConsumer ist ähnlich wie der Consumer, nur dass er zwei Argumente annimmt, etwa von einer HashMap. Wieder kann man bei der forEach Methode schauen, was benötigt wird, und sieht, dass die Methode ein BiConsumer benötigt.

funcint3.PNG

Map<String, Integer> personen = new HashMap<>();
personen.put("Hans", 20);
personen.put("Wurst", 22);
personen.put("Gerald", 34);
personen.forEach((name, alter) -> System.out.println(name + 
" ist " + alter + " Jahre alt"));

Output:
Wurst ist 22 Jahre alt
Gerald ist 34 Jahre alt
Hans ist 20 Jahre alt

Das Supplier Interface

Der Supplier ist das Gegenteil vom Consumer und absolut selbstlos. Er will überhaupt keinen Parameter, liefert aber dennoch einen Wert zurück.

Das Interface sieht so aus:

funcint4.PNG

Es braucht in der Methode get() keinen Parameter, gibt aber irgendetwas zurück.

Das einfachste Beispiel erstellt einfach einen Supplier, der irgendetwas zurückgibt, etwa in einem Lambda. Nicht vergessen, beim Supplier den Typ mit eckigen Klammern mitzugeben.

Supplier<String> halloSupplier = () -> new String("Hallo");
String halloString = halloSupplier.get();

Das Predicate Interface

Ein Predicate ist eine Funktion, die Inputwerte erhält und nach einer gewissen Logik ein true oder false zurückgeben, also einen Boolean.

Zum Beispiel kann man prüfen, ob ein Name mit „W“ anfängt.

List<String> personen = Arrays.asList("Hans", "Wurst", "Gerald");
List<String> namenMitW = namen.stream()
.filter(name -> name.startsWith("W")).collect(Collectors.toList());
namenMitW.forEach(name -> System.out.println(name));

Output: 
Wurst

Das Operator Interface

Das Operator Interface ist eine spezielle Funktion, die den gleichen Typ als Input wie auch als Output hat.

Die Methode „replaceAll“ etwa möchte einen UnaryOperator haben.

funcint5.PNG

Was übrigens auch neu ist, ist der Zugriff auf bestimmte Methoden mit dem Doppelpunkt, wie im Beispiel mit String::toUpperCase. (Wenn man in Eclipse „String::“ eingibt und dann CTRL + Leertaste drückt kommt eine Liste von Methoden und man kann einfach eine auswählen)

List<String> personen = Arrays.asList("Hans", "Wurst", "Gerald");
namen.replaceAll(String::toUpperCase);

Output: 
HANS
WURST
GERALD

Echt genial und gar nicht mal so kompliziert, wenn man mal ein paar einfache Beispiele gesehen hat, oder?