So schön es ist, ein neues Projekt mit der angesagtesten, modernsten Technologie umzusetzen, auch ältere Projekte wollen gepflegt, gewartet und erweitert werden. Diese sind oft nicht mehr ganz so hip umgesetzt und können aus Zeit- und Kompatibilitätsgründen nicht auf neuere Frameworks portiert werden.
So ist es mir mit einer Android-App ergangen. Diese wurde mit Java in der Version 6 entwickelt und sollte weiterhin bis zur API 10 (Android 2.3.3) rückwärtskompatibel bleiben. Als neues Feature sollte es möglich sein, die Daten in einer Listenansicht mit einem Suchbegriff zu filtern oder nach verschiedenen Kriterien zu sortieren.
Eine Collection zu filtern kann in Java ganz bequem umgesetzt werden:
public List<String> filter(List<String> list, String term) { return list .stream() .filter(item -> item.contains(term)) .collect(Collectors.toList()); }
Schade nur, dass Streams erst ab Java 8 unterstützt sind; und seien wir einmal ehrlich: So schön ist es nun auch wieder nicht zuerst die Liste mittels .stream() filterbar zu machen, damit sie nach dem Filtern mit .collect(Collectors.toList()) wieder in eine Liste zurückgeführt werden muss.
Wie könnte ich denn nun diesen Filtervorgang in Java 6 umsetzen? Immerhin gibt es schon seit Java 5 den enhanced for loop. Damit könnte das z.B. so aussehen:
public List<String> filter(List<String> list, String term) { List<String> filteredList = new ArrayList<String>(); for (String item : list) { if (item.contains(term)) { filteredList.add(item); } } return filteredList; }
Gut, das könnte schlimmer sein. Doch nach einigen Ausflügen in Funktionale Bereiche oder der einen oder anderen Webapplikation fühlt sich das doch etwas rostig an. In Typescript wäre das zum Beispiel wesentlich kompakter:
const filter = (list: string[], term: string) => list .filter(item => item.includes(term));
Etwas in dieser Art wäre mir persönlich lieber. Ausserdem muss ich ja nicht nur filtern, sondern die Listen auch umsortieren z.B. aufsteigend nach Länge der Strings, was wiederum in Java 6 so aussehen würde:
public void sort(List<String> list) { Collections.sort(list, new Comparator<String>() { @Override public int compare(String o1, String o2) { return o1.length() - o2.length(); } }) }
Das ist zwar noch überschaubar, macht mich aber nicht glücklich. An dieser Stelle fände ich es schöner, wenn ich einen Lambda-Ausdruck verwenden könnte, wie sie seit Java 8 ja ebenfalls zur Verfügung stehen.
Glücklicherweise wurde mit Android Studio 3.0 Unterstützung für Kotlin in Android Projekten eingeführt (mittels Plugin war das ausserdem bereits mit älteren Versionen von Android Studio möglich). Da Kotlin auch auf einer JVM für Java 6 funktioniert, lässt mich das hoffen, hier einen Ausweg aus meinem ich-will-das-modern-umsetzen-muss-aber-auf-Java-6-bleiben-Dilemma zu finden. Denn Kotlin bietet viele Vorteile: Lambdas, Higher-Order-Functions, null-Safety, Type-Inference und Extension-Functions um mal einen kleinen Teil davon aufzuzählen.
Die Installation von Kotlin ist denkbar einfach. Dafür müssen lediglich zwei Gradle-Skripts angepasst werden.
// build.gradle des Projekts buildscript { ext.kotlin_version = '1.1.60' repositories { jcenter() } dependencies { // ... classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version" } }
// build.gradle des App-Moduls apply plugin: 'kotlin-android' // ... dependencies { // ... compile "org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version" }
Diese Anpassung wird von Android Studio auch automatisch vorgeschlagen, sobald eine Kotlin-Datei angelegt wird.
Dafür kann einfach auf einem Package nach einem Rechtsklick New > Kotlin File/Class angewählt werden. Für dieses Beispiel wähle ich als Kind File und als Namen ListUtils und bestätige mit OK.
Nun müssen nur noch die gewünschten Funktionen in Kotlin umgesetzt werden:
fun filter(list: List<String>, term: String) = list.filter { it.contains(term) } fun sortByLengthAsc(list: MutableList<String>) = list.sortBy { it.length }
So gefällt mir das schon viel besser!
Da Java und Kotlin interoperabel sind, kann diese Hilfsklasse direkt aus dem bestehenden Java-Code aufgerufen werden.
// Beim Filtern wird eine neue Liste angelegt List<String> filterdList = ListUtilsKt.filter(list, "foo"); // Beim sortieren wird die Reihenfolge in der ursprünglichen Liste sortiert ListUtilsKt.sortByLengthAsc(list);
Um mich endgültig zu vergewissern, dass Kotlin das Werkzeug meiner Wahl werden könnte, habe ich ein Android-Smartphone ausgegraben, welches mit Android Version 2.3.6 prädestiniert ist, ein Leben als Türstopper zu fristen.
Siehe da, die App mit Sortier- und Filterfunktion läuft widerstandslos.
Kotlin, mich hast du überzeugt!
Spannend und horizonterweiternd! Danke Thomas für diesen Einblick in deine Arbeit.