Einleitung
Fast jeder kennt das unangenehme Gefühl, einer wildfremden Person zuzuwinken, weil man kurz dachte, die freundliche Geste gälte einem selbst. Galt sie natürlich nicht, sondern der Person, die sich in direkter Linie hinter der eigenen Schulter im toten Winkel versteckte. Auf der Google I/O 2017 habe ich erlebt, wie sich ein ganz ähnliches Empfinden einstellt, wenn man eine halbe Stunde lang in eine Fernsehkamera blickt, die in etwas mehr als Armeslänge vor einem positioniert ist. Selbstverständlich wusste ich, dass es keinen Grund gab, mich in dieser Weise zu portraitieren. Doch nach fünf Minuten ... und weiteren zehn ... es konnte doch nicht?
Konnte es natürlich nicht. Es ging um einen 17-jährigen High-School Studenten, den der Lebensweg seiner Eltern als Einwanderer aus Afghanistan und die in Amerika erlebte Hilfsbereitschaft inspiriert hat, selbst etwas zurückzugeben. In der Reihe vor mir. Sein Projekt verbesserte ohne teure Hardware die Aussagefähigkeit von Mammographien mithilfe von künstlicher Intelligenz. Die von ihm programmierten Routinen erkennen Muster in den Bildern und können so auch ohne medizinisches Fachpersonal präzisere Vorabdiagnosen zur Verfügung stellen.
Eine knappe halbe Stunde zuvor gab es eine Ankündigung, die mir diesen Tag auch ohne die bemerkenswert große Kamera im Gedächtnis gehalten hätte: Die Ankündigung von Stephanie Saad Cuthbertson (Director Product Management Android zu der Zeit), Kotlin zu einer vollständig unterstützten Programmiersprache für Android zu erklären und mit dem Entwickler JetBrains eine Gesellschaft zur Weiterentwicklung der Sprache zu gründen. Kotlin ist eine vergleichsweise junge Programmiersprache, deren erste stabile Version 2016 erschien. Seit 2011 wird sie von einem Team des Herstellers der Entwicklungsumgebung IntelliJ weiterentwickelt und gepflegt. Hier besteht auch die offensichtliche Verbindung zu Java und Android, denn auch die von Google primär unterstützte IDE (Integrated Development Environment oder Entwicklungsumgebung) Android Studio basiert auf IntelliJ.
Dadurch war auch gewährleistet, dass es in Bezug auf die Tools keiner großen Umgewöhnung bedurfte. Und so haben sicherlich einige Android-Entwickler nach dieser Ankündigung 2017 ihre ersten Schritte in Kotlin mithilfe der automatischen Konvertierungsfunktion gemacht und schnell eines erkannt: Die neue Sprache löst sofort Probleme im bestehenden Code ihrer Anwendung:
Der Code wird kürzer und übersichtlicher
Der Code wird sicherer
Es ist kein Problem, Kotlin und Java miteinander in einem Projekt einzusetzen
Keine Semikolons(!)
Oder, um es mit den Worten von Google und Jetbrains zu sagen: Wichtige Merkmale von Kotlin sind: Es ist effektiv durch präzise Schreibweisen (concise), sicher (safe) und gut mit verschiedenen Umgebungen zu integrieren (interoperable).
Wie das in der Praxis aussieht und warum ich schon nach den ersten Arbeitstagen mit Kotlin kein Java mehr sehen mochte, versuche ich in diesem Buch zu erklären.
Ausrichtung des Buchs
Dieses Buch soll Entwicklern einen Einstieg in die Programmiersprache Kotlin ermöglichen. Es konzentriert sich auf die Grundlagen der Sprache und ihre Strukturen, Befehle und Sprachfeatures. Zusätzlich zur Erläuterung der ursprünglichen Aufgabe als ein verbesserter Java-Ersatz wird das aktuelle Haupteinsatzfeld Android behandelt und wie Kotlin die Android-Entwicklung verbessern – oder neutral: verändern – kann. Dabei werden aber Kenntnisse von Android vorausgesetzt, um den Fokus auf die Sprache Kotlin beizubehalten.
Das vorliegende Buch möchte dazu motivieren, vor einem Volleinstieg in Kotlin die Konvertierung Schritt für Schritt zu probieren. Das Stichwort lautet Interoperabilität. Auch in einem großen Java-Spring-Projekt können einzelne Features oder Klassen in Kotlin geschrieben oder in der Webanwendung einzelne Controller oder Datenklassen ergänzt werden. Bei den Beispielen benutze ich wenig Framework-Code und fokussiere mich auf die Umsetzung der Logik in Kotlin. Wenn, dann wird vor allem Android verwendet.
Generell ist es aber gut möglich, auch sein favorisiertes JavaScript- oder TypeScript-Framework weiterzunutzen. Je elaborierter die Build- und Deployment-Prozesse jedoch sind, desto weniger kann ich als App-Entwickler Details der Umsetzung beleuchten.
Darüber hinaus wagt das Kapitel 11 einen Ausflug in die Welt der Cross-Plattform-Entwicklung. Das ist wichtig für alle, deren Code in Zukunft aus einer Kotlin-Codebasis nicht nur auf Android-Smartphones oder im Browser laufen soll.
Zielgruppe
Dieses Buch ist für alle geschrieben, die sich für die Entwicklung mit Kotlin interessieren und die bereits grundsätzliche Kenntnisse in der Softwareentwicklung haben.
Dazu gehört zum Beispiel, dass Sie grundlegende Quelltext-Elemente wie Variablen, Konstanten, Kontrollstrukturen und Funktionen kennen und Ihnen Begriffe aus der objektorientierten Entwicklung wie Klasse, Objekt und Instanz geläufig sind. Programmierparadigmen und Muster der Softwareentwicklung werden ebenfalls nicht ausführlich erklärt, sondern vor allem dort erwähnt, wo es zum Verständnis der Unterschiede zwischen Kotlin und seinen Alternativen dient. Einblicke in die Programmiersprache Java helfen, sind aber nicht zwingend erforderlich.
Die meisten Entwickler sammeln ihre ersten Programmiererfahrungen in Kotlin nicht als Einsteiger in die Softwareentwicklung. Sie gehen mit Erwartungen aus bekannten Sprachen oder sogar mit existierendem Code an die Arbeit.
Mit Kotlin lässt sich in dieser Situation besonders gut arbeiten, denn es erfordert für viele Entwickler (Java, Android) keinen Umstieg auf eine andere Entwicklungsumgebung und verträgt sich mit bestehenden Infrastrukturen. Durch die hohe Flexibilität im Bereich der Interoperabilität ist es eine gute Strategie, in bestehendem Code einfach einmal einen konkreten Anwendungsfall zu ergänzen oder einzelne Klassen zu ersetzen. Damit können Entwicklungsteams eine informierte Entscheidung treffen, ohne zwangsläufig vor einem kompletten Neuanfang zu stehen.
Aufbau des Buchs
Der Aufbau dieses Buchs folgt meinen Erfahrungen als Hands-On-Entwickler und Geschäftsführer einer Agentur, in der Softwareentwickler verschiedener Erfahrungsstufen arbeiten.
In unserem Team haben alle Kotlin auf der Basis von Android kennengelernt. Sie waren und sind erfahrene Java-Entwickler und Experten in der Umsetzung von Apps. Wichtige Muster aus der App-Entwicklung und auch die Best Practices in Android waren also von vorneherein bekannt. Folgerichtig handelte es sich bei den ersten Experimenten denn auch eher um Java ohne Semikolons. Auf Basis einer fundierten Kenntnis der APIs und Tools für die Android-Entwicklung hat sich erst im Verlauf der Projekte ein tieferes Verständnis für die weitergehenden Möglichkeiten von Kotlin entwickelt. Das scheint mir typisch für eine Sprache, die entwickelt wurde, um den Unzulänglichkeiten einer existierenden Vorgängerin zu begegnen. Ich werde deshalb auf den folgenden Seiten ähnlich vorgehen.
In Kapitel 1 gebe ich zunächst einen Überblick über die Eigenschaften und Vorteile von Kotlin, seine Konzepten und die positiven Effekte auf die eigene Codebasis.
In Kapitel 2 erläutere ich kurz die zur Verfügung stehenden Tools, die ich für die ersten Schritte empfehle.
Kapitel 3 enthält kommentierte Beispiele, um zunächst ein Gefühl für die Sprache zu geben und ihre Möglichkeiten zu erkunden. Für diejenigen Leser, die bereits Vorkenntnisse in Android oder zum Beispiel Java haben, wird dieser Einstieg vielleicht für die ersten eigenen Versuche ausreichen. Einsteiger können dieses Kapitel auch erst mal überspringen. In den darauffolgenden Kapiteln folgt die systematische Betrachtung der einzelnen Sprachelemente.
Kapitel 4 stellt die grundlegende Syntax von Kotlin in den Mittelpunkt, und konzentriert sich auf die Grundstrukturen von Programmen und die Datentypen.
Wer direkt sehen möchte, wie Kotlin funktioniert, dem sei schon an dieser Stelle https://kotlinlang.org empfohlen. Unter dem Menüpunkt Learn finden sich dort die sogenannten Kotlin Koans – kleine Übungen, die direkt im Browser kompiliert und getestet werden. Darüber hinaus zeigen sie auch, wie man von Anfang an mit Tests sinnvolle Validierungen der eigenen Überlegungen durchführen kann. Insofern ein Must-Read (and try) für den angehenden Kotlin-Entwickler.
Auch die Beispiele aus dem Buch können aus dem GitHub-Repository dorthin kopiert, ausprobiert und variiert werden.
In Kapitel 5 werden die Kontrollstrukturen vorgestellt. Es geht also um Verzweigen und Wiederholen – und weitere daran angelehnte Sprachfeatures.
Das Kapitel 6 widmet sich den Funktionen und ihren verschiedenen Sonderformen. Ich werde dort eine längere Zeit mit höheren Funktionen und Lambdas verbringen, aber auch die Grundlagen von Funktionen ausführlich erklären.
Darauf folgt in Kapitel 7 ein ausführlicher Block zu den objektorientierten Anteilen von Kotlin. Hier geht es um alle Besonderheiten von Klassen, Objekten und Funktionen im Zusammenspiel. Dafür wird neben einzelnen Code-Schnipseln ein erstes Projekt vorgestellt.
Dieses Projekt beinhaltet eine Expression-Bibliothek, die in der Lage ist, mathematische Ausdrücke zu interpretieren und auszurechnen. Bislang liegt dieser Open-Source-Code in Objective-C, Swift und Java vor, in diesem Kapitel entsteht eine Kotlin-Version sowie eine Kommandozeilen-Anwendung, die sie nutzbar macht.
Kapitel 8 ist dem Thema automatische Tests in Kotlin gewidmet. Für die Nutzung der Test-Frameworks werden die Inhalte der vorherigen Kapitel als Handwerkszeug benötigt. Die Tests selbst beziehen sich vor allem auf das Testen von Funktionen und Klassen.
Kapitel 9 steigt tiefer in die komplexeren Konzepte von Kotlin ein, die auf dieser Basis aufbauen. Thema ist hier die Organisation von größeren Projekten durch Architekturmuster. Die Nebenläufigkeit mit Kotlin-Coroutines nimmt einen guten Teil des Kapitels ein und last but not least bespreche ich die Veränderungen im Denken und im Code, die sich aus der Kombination von funktionalem und objektorientiertem Stil ergeben.
Kapitel 10 ist Android gewidmet – hier entsteht eine vollständige App in Kotlin. Auch wenn das Kapitel mit den Grundlagen beginnt, ist es hier nicht möglich, alle Aspekte und Bibliotheken von Android zu behandeln. Der Schwerpunkt liegt auf moderner Architektur und den Anteilen, die sich durch Kotlin besser umsetzen lassen.
Kotlin ist seit der Version 1.2 in Bezug auf Interoperabilität einen Schritt weitergekommen und bietet auch Optionen zum Kompilieren in JavaScript und nativen iOS-Code. Dazu mehr in Kapitel 11.
Downloads zum Buch
Im Repository https://github.com/kotlin-karlszwillus finden sich Android-Beispiele, die Kotlin-Portierung der Expression-Bibliothek und die Listings aus den anderen Kapiteln zum Ausprobieren, mit Sourcecode-Dokumentation.
Wer mit GitHub nicht vertraut ist, kann den Großteil der Inhalte auch über die Website des Verlags herunterladen: www.mitp.de/853
Über die Projekte in diesem Buch
Es gibt im Buch kein durchgehendes Projekt, mit dem alle verschiedenen Bereiche von Kotlin gestreift werden. Vielmehr habe ich aus der Praxis verschiedene Versatzstücke aus Projekten herausgelöst, die einzelne Schritte und Techniken der jeweiligen Kapitel illustrieren. So können Sie sich auch einzelne Themen im Buch ansehen, ohne sich in ein großes Beispielprojekt eindenken zu müssen.
Am Anfang des Buchs stehen noch die einzelnen Anweisungen und Ausdrücke im Mittelpunkt, hier handelt es sich um einfach gewählte Beispiele.
Damit ich in der Lage bin, auch komplexere Aufgaben zu zeigen, benutze ich eine Open-Source-Bibliothek, die ich einmal von der Kommandozeile und später auch in einer App verwende. Sie stellt eine vollwertige Sprache zum Lösen von Ausdrücken innerhalb von Formularen oder anderen Anwendungen zur Verfügung. Ihr Name ist NRExpressionLib, sie stammt vom iOS-Entwickler Nikolai Ruhe aus Berlin – wir haben in mehreren Projekten zusammengearbeitet. Da unsere Firma sie bereits von Swift nach Java portiert hat, gehe ich nun einen Schritt weiter und konvertiere sie in die Sprache Kotlin. Diese Aufgabe ist kein klassischer Einstiegspunkt, doch die Konvertierung, auch mithilfe der automatischen Konvertierungsfunktion, führte auch mich zu einigen interessanten Herausforderungen, an denen sich zum Beispiel die Inkompatibilitäten zwischen der Handhabung von Generics und den strengen Regeln für Typen bei Kotlin zeigen lassen.
Ein zweites Projekt stammt aus der Praxis und bildet eine Bibliothek von Meditationsübungen ab. Aufgaben wie Navigation und die Darstellung von Listen kann ich anhand dieser Listings einfacher demonstrieren. Diese App steht nicht als vollständiges Codebeispiel zur Verfügung, sondern lediglich in Auszügen, bzw. einzelnen Komponenten, die ausprobiert werden können. Sie ist aber im Play Store als »Key Meditation« kostenlos verfügbar.
Nicht alle Listings im Buch stammen aus diesen beiden Projekten. Einige stehen einfach für sich oder kommen in leicht abgewandelter Form aus dem Arbeitsalltag eines App-Entwicklers.
Die Codebeispiele im Buch sind daher Auszüge aus längeren Listings, die auf der Seite des Verlags unter www.mitp.de/853 und im öffentlichen Repository unter https://github.com/kotlin-karlszwillus zur Verfügung stehen.
Auf eine vollständige Befehlsreferenz habe ich zugunsten der bekannten Online-Quellen verzichtet, der Anhang A enthält aber kürzere Übersichten zu den Standardpackages, die sich auch plattformübergreifend verwenden lassen.
Eine Mischung von Deutsch und Englisch lässt sich nicht ganz vermeiden. Bei Fachbegriffen gebe ich den englischen Versionen den Vorzug und verwende eingedeutschte oder übersetzte Bezeichner nur dort, wo sie vergleichbar allgemeingültig sind.
Was nicht in diesem Buch steht
Jeder Autor entscheidet über Schwerpunkte und Inhalte eines Buchs, doch für den einen oder anderen mag es wichtiger sein, welche Themen nicht behandelt werden. Für mich sind dies in aller Kürze: keine RX-basierte Entwicklung, so gut wie keine experimentellen Features (mit Ausnahmen bei Coroutines und Contracts), kein Kotlin/Native und nur wenig über Multiplattform-Projekte.
Kotlin ist eine Sprache, deren Entwicklung noch in vollem Gang ist. Dabei werden vor allem auch im Bereich der Interoperabilität immer wieder neue Welten erschlossen. Mit der Version 1.2 ist Kotlin/Native dazugekommen, ein Setup für die Anbindung verschiedener Umgebungen, die auf C und moderneren Ableitungen davon basieren. Damit ist theoretisch ein Einstieg in die Entwicklung von iOS- und Desktop-Varianten möglich. Hierfür sind zum jetzigen Zeitpunkt noch sehr viel Wissen und Basteltalent in den ursprünglichen Sprachen notwendig, das mir fehlt. Ich freue mich aber auf weiteren Fortschritt in diesem Bereich.
Das zweite Thema, das lediglich gestreift wird, sind Multiplattform-Projekte. Tatsächlich ist ein Projekt in einem solchen Szenario umgesetzt, doch die Unterstützung ist auch in der Version 1.3 noch als experimentell gekennzeichnet. Aus diesem Grund habe ich mich entschieden, auch diesen Teil nur anzureißen und für eine spätere Ergänzung aufzuheben.
Zuletzt habe ich persönlich keine starke Zuneigung zur reaktiven Programmierung mit RxJava oder RxKotlin, auch wenn sie ihre Vorteile hat und auch regelmäßig Teil von datenintensiven Projekten ist. In der vorliegenden Ausgabe war mir der Fokus auf den Kotlin-eigenen Mechanismen im Bereich Coroutines, Observer und Mapping wichtiger. Für Android habe ich die Vorstellung von LiveData und ViewModel vorgezogen, die in dem gleichen Bereich im Einsatz sind, der heute häufig von Rx-Komponenten abgedeckt wird.
Weiterentwicklung von Kotlin
Ein wichtiges Anliegen habe ich in Bezug auf Aktualität und Updates der Sprache: Selbstverständlich werden sich von der Erstellung dieses Manuskripts bis zur Veröffentlichung Dinge ändern.
Insbesondere im Bereich von Bibliotheken verändern sich Möglichkeiten und APIs schnell und häufig. Zwar habe ich darauf geachtet, welche Veränderungen zur Erstellung des Manuskripts bereits absehbar waren, doch bereits mit dem nächsten größeren Update können sich auch grundlegende Mechanismen ändern.
Als Beispiel seien hier die Coroutines genannt, die mit der Version 1.2 eingeführt wurden und ein Ersatz für Java-basiertes Multithreading sind. Daher möchte dieses Buch vor allem die Grundlagen vermitteln, mit denen Sie funktionierenden, gut strukturierten und durchdachten Kotlin-Code erstellen können.
Über den Autor
Meine eigene Ausbildung nennt sich Medienwirt – ein Diplomstudium, das mir weitreichende Freiheiten und Interpretationsmöglichkeiten gewährt hat. So konnte ich die Programmierung, bis dahin Hobby und Schulfach, auch an der Universität weiterverfolgen, dann unter dem Stichwort »Neue Medien«. Heute arbeite ich als Geschäftsführer der Agentur Karlmax Berlin an verschiedenen Kundenprojekten im Bereich Apps.
Meine tägliche Arbeit hat bis vor Kurzem auch noch stark in den Projekten stattgefunden, sodass ich auch mit Kotlin dort meinen ersten Kontaktpunkte hatte.
Unsere Agentur hatte das Glück, dass wir mit zwei Kunden zum Zeitpunkt der Bekanntgabe von Kotlin als offizieller Entwicklungssprache für Android bereits über ein Jahr an Projekten mit Kotlin arbeiten durften. Die positiven Einflüsse auf eine Android-App kannten wir so bereits durch konkrete Projekte. Nicht zuletzt mit dieser Erfahrung im Rücken möchte ich alle Entwickler aus dem Java- und Android-Umfeld einladen, sich mit Kotlin zu beschäftigen.
Kapitel 1:
Eigenschaften von Kotlin
When I visited Jetbrains for the first time in Spring 2010, I came fairly certain that the world didn't need another general-purpose programming language.
– Andrey Breslav
1.1 Eine neue Sprache
Braucht es noch eine neue Programmiersprache auf JVM-Basis? Diese Frage stellt Andrey Breslav in der Einleitung von Kotlin in Action, wenn er seine Entscheidung begründet, nach dem Kennenlernen des Projekts Kotlin im Jahr 2010 als Chefentwickler und Architekt in das Projekt einzusteigen. Wären Sie auch dieser Meinung, dann würden Sie dieses Buch jetzt wahrscheinlich nicht in der Hand halten. Was meine Gedanken zu dieser Aussage sind, soll auch Sie noch einmal darin bestärken, dass die Entscheidung für Kotlin eine kluge ist:
Java ist eine populäre Sprache in der Lehre und hat auch seine Nischen in Server- und Mobileanwendungen gefunden. Doch seit Jahren existieren bereits vielfältige Ansätze, die inhärenten Probleme der Sprache zu umgehen oder Sprachelemente zu modernisieren. Aufgrund der auch durch Rechtsstreitigkeiten zwischenzeitlich verlangsamten Java-Entwicklung fanden diese jedoch wenig Berücksichtigung im Sprachstandard. Umgebungen wie Android sind aus verschiedenen Gründen zur Kompatibilität mit alten Versionen verdammt.
Aus diesen Gründen hat sich die Sprachentwicklung in Derivate und auf Java-Technologie basierende Komponenten verlagert. Bekannte Beispiele für diesen Sprachansatz sind Scala, Clojure oder Groovy. Dazu kommen zwei gute Dutzend weitere Varianten, die sich entweder in bestimmten Branchen durchgesetzt haben oder zur Lösung spezieller Aufgaben im Scripting oder als Definitionssprachen dienen. Und seit einigen Jahren tummelt sich auch Kotlin in dieser illustren Menge.
Bei der ersten Präsentation ihrer neuen Sprache nannten die Entwickler von JetBrains Scala als ihre Referenz. Scala ist zwar mit den richtigen Sprachfeatures ausgestattet, aber mit einem zu langsamen Compiler geschlagen. Darüber hinaus gilt Scala nicht unbedingt als einsteigerfreundlich; es handelt sich im Gegenteil um eine der Sprachen, die Einsteigern nie suggeriert, mehr verstanden zu haben als sie selbst glauben.
Als langjähriger Entwickler von IDEs für Java hatte das JetBrains-Team eine klare Vorstellung von den Problemen existierender Java-Software auf der einen und den Wünschen der Entwickler auf der anderen Seite. Insofern stellt Kotlin aus der Perspektive seiner Erfinder auch eine Möglichkeit dar, Entwickler an ihre Tools zu binden und ihnen neben der Entwicklungsumgebung auch auf der Sprachebene einen voll kompatiblen Ersatz zu bieten. Interoperabilität mit 100% Java-Kompatibilität war das Ziel.
Wenige Jahre später folgten erste Komponenten für die Arbeit mit Android, und spätestens seit der Zusammenarbeit von Google und JetBrains in der Etablierung von Android Studio als Standard-IDE für die Entwicklung von Apps schien die erfolgversprechendste Nische von Kotlin besetzt.
Das Versprechen der Interoperabilität beschränkt sich nicht auf Komponenten innerhalb von Java oder anderen Java-basierten Frameworks. Konsequent wird auch die Kompilierung auf andere Plattformen vorangetrieben, wie zum Beispiel in Desktop-Anwendungen, JavaScript oder auch natives iOS.
Neben den Gemeinsamkeiten gilt es jedoch auch, mit Kotlin neue Konzepte zu verstehen und den Stil der entstehenden Software anzupassen. Drei wesentliche Spracheigenschaften sollten Entwickler im Kopf haben, wenn sie sich an die Arbeit mit Kotlin machen:
Kotlin ist objektorientiert und funktional.
Es verwendet und verlangt statische Typen.
Im Gegensatz zu Java ist Kotlin Null-Safe.
Java wurde lange Zeit objektorientiert und imperativ benutzt und gelehrt – zumindest bis zur Version 8, die erste Aspekte funktionaler Programmierung eingeführt hat. Im Gegensatz dazu ist der beherrschende Stil von Kotlin die funktionale Programmierung. Dazu gehört, Programmcode in Funktionen zu differenzieren und unveränderliche Datenstrukturen zu bevorzugen. In der Koordination von Lese- und Schreibzugriffen und dem Management von Abhängigkeiten im Quellcode liegen in der Praxis immer wieder Probleme der imperativen und auch der objektorientierten Programmierung.
Die größten davon sind unerwünschte Seiteneffekte und implizite Abhängigkeiten zwischen Methodenaufrufen und Objektzuständen.
In der Objektorientierung ist es gute Praxis, ein Objekt möglichst vollständig nachzubilden und die zentralen Eigenschaften oder Attribute mit den Aktivitäten oder Methoden zu gruppieren, die den Zugang zu den Daten schaffen. In dieser Logik ist die Klasse der statische Rahmen aller Objekte des gleichen Typs. Sie stellt sicher, dass alle Instanzen die dort bestimmten Eigenschaften erfüllen und Daten bereitstellen. All diese Mechanismen unterstützt Kotlin und verbessert sie im gleichen Schritt.
1.2 Kotlin und Android
Hier kann man guten Gewissens sagen, dass Android zu dem Zeitpunkt, als die Entscheidung fiel, Kotlin als möglichen Ersatz in ersten Demos zu präsentieren, ein besonders dankbarer Kandidat für weitreichende Verbesserungen war.
Das lässt sich gut am Beispiel der Android Activity nachvollziehen. Diese Klasse ist ein sehr komplexes Stück Code, sie modelliert die Repräsentation eines »Screens« auf dem Smartphone und stellt den zentralen Anbindungspunkt für das Verhalten einer App dar. Es handelt sich um eine Klasse von 8.000 Zeilen mit etwa 40 Attributen, hinter denen zum Teil wieder komplexe Strukturen stecken. Teilweise setzen sie auch Nebenläufigkeit voraus oder werden durch das System regelmäßig ausgewertet oder beschrieben.
Und auch, wenn diese Klasse selbst in den Apps lediglich benutzt wurde, so schaffte sie mit ihren umfangreichen Schnittstellen die Grundlage für toxische Entwicklungsmuster.
In den ersten Versionen von Android gab es darüber hinaus wenig Vorgaben, nach welchen Mustern der Quellcode einer App zu strukturieren ist. Der übliche Aufbau begann mit der Initialisierung der App-Zustände in der Methode public
void
onCreate(Bundle
savedInstanceState)
in einer von Activity
abgeleiteten Klasse. Diese Methode, vom Android-System aufgerufen, war und ist der kanonische Startpunkt einer App. Auch die Handhabung weiterer Komponenten folgte dann in dieser Klasse, herausgezogen lediglich die Views und ihre Backing Objects wie Adapter und Datenbanken.
Die gesamte Komplexität hing damit von Anfang an in jeder einzelnen Komponente einer App, die auf dieser Basis entwickelt wurde. Aufgrund der Funktionsfülle hatten auch die abgeleiteten Klassen eine Tendenz zu unkontrolliertem und schlecht kontrollierbarem Wachstum. Da nehme ich mich persönlich gar nicht aus.
Um nun eine einzelne Methode dieser Klasse sinnvoll per Unit-Test validieren zu können, bedurfte es nicht nur einer Instanz der entsprechenden Activity
, sondern mindestens noch eines Aufrufs der Methode onCreate()
. Im Normalfall benötigte dieser Aufruf eine komplette Systemumgebung (vulgo Smartphone oder Simulator), da diese Methode in der Vererbungshierarchie auf verschiedene Komponenten zugreift, die im Normalfall auf einem eingeschalteten Smartphone zur Verfügung stehen. In einem Unit-Test, dem kein Android-Gerät zur Verfügung steht, zog dies entweder aufwändiges Mocking von Komponenten nach sich – oder den Verzicht auf Unit-Tests.
Das Ergebnis waren zahlreiche ungetestete bis schlecht getestete Apps und damit eine deutlich gefühlte Unterlegenheit gegenüber Umgebungen, in denen die gute Testbarkeit des Codes eine lange geübte Selbstverständlichkeit darstellte.
Zunächst sind an dieser Stelle Anbieter von bekannten Apps eingesprungen, die durch das Sponsoring von Open-Source-Entwicklungen für Abhilfe gesorgt haben. Sie führten Techniken und Strukturen wie die Architekturmodelle MVP und MVVM für Android ein und vereinfachten die Organisation von Abhängigkeiten mit Dependency Injection oder auch konkrete Aufgaben wie Bilderhandling und Netzwerk-Kommunikation. Mit den entsprechenden Libraries von Square, Facebook und anderen hat wohl jeder Android-Entwickler mehr als einen kurzen Flirt verbracht. Immerhin sorgten sie für strukturierteren Code.
Google verbesserte Android jedoch in den letzten Jahren durch neue Architekturansätze und leichtere Komponenten auf dieser Seite stark und machte viele der oben genannten Komponenten zu Teilen des Frameworks. Das ist aber nur eine Seite der Medaille.
Denn der Code, der auf diesen komplexen Bestandteilen aufsetzt, soll sich auf seine eigenen Qualitäten und Aufgaben fokussieren. Wenn das mobile Betriebssystem schon so große Teile der Aufgaben abnimmt, dann kann der eigene Code möglichst reduziert und möglichst präzise sein. In der Zwischenzeit hat sich der Blick dafür auf das Paradigma der funktionalen Entwicklung gerichtet.
1.3 Vorteile von Kotlin
Auf ganz grundsätzliche Art können die Sprachkonstrukte aus der funktionalen Entwicklung die Testbarkeit erleichtern oder sogar forcieren. Und diese Aspekte führt Kotlin als grundlegenden Bestandteil seiner DNA mit sich.
Eine »reine« (pure function) Funktion, die von außen alle nötigen Daten zur Bearbeitung als Parameter erhält und dann ein Ergebnis zurückgibt, ohne Daten an anderer Stelle zu manipulieren, kann leicht getestet werden. Je mehr Querbeziehungen zu anderen Daten oder Objekten bestehen oder wenn komplexere Systemzustände in die Ausführung der Funktion hineinspielen, desto schwieriger wird es, den einen Fall zu isolieren. Wenn sich aber der eigene Code darauf verlassen kann, dass die standardisierten Bibliotheken gut getestet und vollständig sind, dann lohnt es sich, auch den eigenen Use-Case möglichst präzise aufzuschreiben.
Die Arbeit mit kurzen Funktionen, unveränderlichen Datenstrukturen und höheren Funktionen führt dazu, dass die eigenen Code-Bestandteile nicht nur kürzer, sondern auch besser testbar werden.
Statische Typisierung und Null-Sicherheit stehen ebenfalls auf der Seite des Forcierens von besserer Wartbarkeit, weil sie Entwicklern die Mittel für den laxen Umgang mit seinen Variablen nehmen. Gerade bei einem komplexen System wie dem Smartphone lag und liegt auch hier großes Potenzial zur Verbesserung.
Wenn die oben vorgestellten Techniken aus der funktionalen Entwicklung und bessere Tests vor allem zur Entwicklungs- und Compilezeit wirken, so bedeuten statische Typen und Null-Sicherheit vor allem bessere Sicherheit zur Laufzeit. Sie bringen Klarheit über den Zustand einer Variable im Verlauf ihrer Benutzung, auch wenn der Code im Einsatz ist. Das gilt umso mehr, wenn der Umstieg von einer dynamisch typisierten Sprache wie JavaScript passiert.
Das jedoch funktioniert nur dann gut, wenn vorhersagbar ist, welchen Wert eine Variable zu einem Zeitpunkt annehmen kann. Veränderliche Datenstrukturen bedeuten bei jeder Zuweisung Verantwortung des Entwicklers für die Zulässigkeit der Operation. In einem System mit unveränderlichen Strukturen kann diese Verantwortung an den Compiler übertragen werden.
Eine Funktion, deren Rückgabewert durch einen Test validiert wird und deren Ergebnis in eine neue Variable geschrieben wird, bedeutet eine testbare Kette von Operationen.
Davon können auch Apps profitieren, denn sämtlicher Quellcode, der auf das Framework Android aufbaut, liegt im Zuständigkeitsbereich des Entwicklers. Eine gute objektorientierte Modellierung, die konsequent Vererbung und Delegation nutzt und intern möglichst auf unveränderliche Datenstrukturen und einzelne Funktionen setzt, kann als aktueller Goldstandard in der App-Entwicklung gelten.
1.4 Ziele der Entwicklung von Kotlin
1.4.1 Übersichtlichkeit, präziser Ausdruck
The best code is no code at all.
– The Internet
Ein Hauptziel der Entwicklung von Kotlin war die Reduktion von Boilerplate-Code. Darunter versteht man Standard-Code oder auch überflüssige Zeichen, die man immer wieder aufschreibt, um grundlegende Funktionen zu ermöglichen. An sich nicht direkt schädlich, führt dieser Überfluss oft dazu, dass die eigentliche Funktion nicht im Mittelpunkt steht, sondern zunächst aus dem Standardcode herausgeschält werden muss.
Kotlin benötigt weniger Zeichen, um komplexe Zusammenhänge aufzuschreiben.
Es gibt verschiedene Strategien, um dieses Ziel zu erreichen, zum Beispiel die Verwendung von Konventionen, Bündelung von Funktionen in Bibliotheken oder das Weglassen von redundanten Zeichen. In Kotlin finden beinahe alle Prinzipien für diese Reduktion Anwendung, zum Teil als optionale Möglichkeiten und zum Teil verbindlich.
Dazu gehören unter anderem:
Vereinfachungsregeln für zahlreiche Aufrufe und Kurzformen
Einzeilige Funktionen zur Rückgabe eines Wertes
Datenklassen, deren Quellcode auf eine Zeile reduziert werden kann
Kurzschreibweisen mit Lambdas und Standardfunktionen
Extension-Funktionen statt Helper/Utility-Klassen
Mapping-Funktionen der Collection-Klassen
Neben den Veränderungen im Quellcode der Anwendung unterstützen darüber hinaus die Sprachfeatures wie Null-Sicherheit und statische Typen die Reduktion des Codes, der zur Behandlung von Laufzeitproblemen bislang notwendig war.
Fairerweise sollte hier auch erwähnt werden, dass manche Konstrukte der funktionalen Programmierung den Quellcode auch verlängern können. Bei ganz einfachen Objektzuständen wird das deutlich. Als Beispiel soll die Modellierung einer einfachen Checkbox dienen, die lediglich zwischen »an« und »aus« unterscheidet. In der klassischen Objektorientierung legt man diesen Schalter als eine Klasse an, die den aktuellen Schaltzustand als Datenfeld hält und eine Methode toggle
zum Anschalten oder Abschalten anbietet.
1 class Switch {
2 var isActive: Boolean = false
3
4 fun toggle() {
5 isActive = !isActive
6 paint()
7 }
8 }
Listing 1.1: Objektorientiertes Einschalten
Die Funktion toggle
ist damit jedoch nicht unabhängig von Daten. Der Aufruf erfolgt ohne Parameter. Das bedeutet, die Funktion manipuliert Daten (den Schaltzustand) und arbeitet daher mit Seiteneffekten, statt mit Input und Output. Das ist in der Benutzung zunächst einmal praktisch und nach objektorientiertem Muster auch sauber und durchaus nicht untestbar.
Der Test jedoch erfordert die Erzeugung eines Objekts. Das ist immer dann einfach, wenn ein Objekt für sich steht und keine weiteren Querbeziehungen hat. Man stelle sich aber vor, diese Checkbox benötigt wiederum einen Android-Kontext, wie es bei allen View-Objekten aus dem Android-Framework üblich ist.
Dann kann der Test auf die eigentlich simple toggle
-Funktion nur noch implementiert werden, indem entweder der komplette Kontext von Android simuliert (mocked) wird, oder der Test direkt auf einem Android-Telefon ausgeführt wird.
In einer funktionalen Welt wäre das nicht gegeben. Die Checkbox wäre modelliert als eine Variable, zum Beispiel auch ein Objekt, das den Wert kapselt. Dazu benötigt es eine Funktion, der dieses Objekt übergeben werden kann, welches dann den Zustand ausliest und ein neues Objekt zurückgibt, das den gegenteiligen Zustand hat.
1 fun toggleSwitch(switch: Boolean) {
2 // Neuen Schalter mit der Negation des Werts von switch erzeugen
3 return Boolean(!switch)
4 }
5 fun paintSwitch(switch: Boolean) {
6 }
7
8 val switch = false
9 val switchedOn = toggleSwitch(switch)
10 paintSwitch(switchedOn)
Listing 1.2: Funktionales Einschalten
In dieser Variante werden die Daten nicht manipuliert. Der abgeschaltete Schalter und sein aktiviertes Pendant sind technisch gesehen zwei verschiedene Variablen. Sie sind auch nicht abhängig von anderen Aufrufen.
Solange die Umsetzung von toggleSwitch
nach einem Interface für schaltbare Objekte geschieht, wäre es darüber hinaus auch einfach, die Funktion zu erweitern und trotzdem eine gute testbare, funktionale Umsetzung zu haben. Was damit allerdings verloren geht, ist der Zusammenhang, den die Klasse Switch vorher geleistet hat.
Natürlich ist das ein Beispiel. Doch genau diese Überlegungen finden in vielen Softwareprojekten statt. In der funktionalen Programmierung sucht man immer die Strategie, die Veränderung zu einer Funktion und ihre Daten zu einem Parameter zu machen und dann ein Ergebnis im neuen Zustand zurückzugeben, der im nächsten Schritt weiter verwendet werden kann.
Die gleiche Logik wird angewendet, wenn Aufgaben bei einer Umsetzung von MVP (Model View Presenter) in einer App in einzelne Schritte zerlegt werden. Die Trennung von Datenmodell, Logik und darstellender Schicht sorgt dafür, dass mehr und kleinere Funktionen mit einer gerichteten Abhängigkeit entstehen. Zum Testen wird nicht mehr das Große Ganze™ benötigt, sondern die einzelne Entscheidung des Controllers kann auch einzeln getestet werden. Der gleiche Controller kann dann – dem obigen Checkbox-Beispiel folgend – eine Checkbox genauso einschalten wie einen Switch (die iOS-Variante) oder die richtige Auswahloption in einem Radio-Button aktivieren.
Umgekehrt gewinnen große Programmstrukturen auch dadurch, dass sie durch die Objektorientierung größere Programmteile zu logischen Einheiten zusammenfassen können.
Daher ist es wichtig, dass Kotlin nicht nur durch den Fokus auf die funktionale Entwicklung gewinnt. Sinnvolle neue Sprachfeatures verbessern auch Muster der klassischen Objektorientierung. Dazu zählen folgende wichtige Aspekte:
Parameter mit Namen und Default-Werten
Ausdrückliche Casts und Konvertierungen, zusammen mit Smart Casts (die Überprüfung des Typs castet automatisch auf das entsprechende Ergebnis)
Finale Klassen als Standard zusammen mit kürzeren Schreibweisen für Klassen und Attribute
Wegfall von Klassenattributen als Variable und Ersatz durch Properties mit Getter/Setter
Was auch noch zum Thema Kürzung und Wegfall von Boilerplate-Code gehört: In den Beispielen teile ich auch praktische Hinweise dazu, welche Kurzformen sich in unserer Praxis der App-Entwicklung als positiv erwiesen haben. Wer noch in APL oder Perl entwickelt hat, wird das Risiko kennen, dass in zu kurzen oder extrem verschachtelten Codestellen steckt: Zu große Verdichtung ist schwierig zu verstehen. Wenn dann im Team an einem größeren Projekt gearbeitet wird, ist es wichtig, dass hier eine gute Festlegung auf gemeinsame Konventionen zur Nutzung der Kurzformen abgestimmt wird.
1.4.2 Sicherheit
Der Begriff Sicherheit bezieht sich bei Kotlin vor allem auf Sicherheit in Bezug auf die Stabilität der eigenen Programmstrukturen. Die vier wichtigen Aspekte zur Erhöhung der Sicherheit sind Null-Sicherheit und statische Typen, Testbarkeit und Vermeidung von Seiteneffekten durch funktionale Programmstrukturen.
An erster Stelle steht der Umgang mit null, also Referenzen auf nicht mehr existierende oder nie initialisierte Objekte oder Variablen. In Java-Projekten nehmen sowohl der Code zur Behandlung von null, als auch die Laufzeitfehler bei der unsauberen Überprüfung von Daten auf null eine große Stellung ein. Je komplexer das System und der Kontext sind, desto mehr finden sich die entsprechenden Verweise im Code oder in den Problemlogs einer Anwendung.
Die wichtigste Errungenschaft von Kotlin ist, dass bereits der Compiler auswerten kann, ob eine Variable oder ein Objekt zum Zeitpunkt seiner Verwendung null ist, oder sein kann. So weiß auch der Entwickler jederzeit, welchen Zustand eine Variable hat. Dadurch kann jeder Zugriff auf eine Variable angemessen abgesichert werden.
Dieser Zugewinn an Sicherheit setzt sich im Bereich der statischen Typisierung fort. Auch hier ist bereits durch den Compiler validiert, welchen Typ eine Variable zu einem bestimmten Zeitpunkt hat. Auch hier liegt der wesentliche Vorteil in der Stabilität und Vorhersagbarkeit einer Variablen. Im Vergleich zu Java sind dabei vor allem Details unterschiedlich, besonders im Bereich der sogenannten Generics und bei bestimmten impliziten oder expliziten Typumwandlungen.
Vergleicht man dieses Sprachfeature jedoch mit einer JavaScript-Anwendung, so wird deutlich, dass es ein großer Schritt ist, da Typisierung dort nur durch Bibliotheken oder TypeScript zu erreichen ist. Dank Typinferenz, also dem Ableiten des Typs aus der ersten Zuweisung, ist der Mehraufwand beim Deklarieren auch überschaubar.
Diese beiden Aspekte von Sicherheit funktionieren insbesondere dann, wenn sie mit der konsequenten Anwendung funktionaler Prinzipien kombiniert werden. Unveränderliche Variablen und Funktionen, die ohne Seiteneffekte auskommen, sondern im Sinne einer reinen Funktion nur über Input und Output mit dem restlichen Code zusammenhängen. Sie sehen, ich beginne, mich zu wiederholen.
1.4.3 Interoperabilität
Das Prinzip der Interoperabilität wird in der Sprache durch zwei wesentliche Punkte umgesetzt:
Kotlin verträgt sich prinzipiell gut mit existierendem Code und ist dafür gedacht, auf bestehende Schnittstellen aufzusetzen. Aufgrund der guten Kompatibilität mit der JVM ist es zum Beispiel gut möglich, Java-Klassen und Kotlin-Klassen in einem Projekt gemeinsam zu integrieren.
Außerdem ist Kotlin dafür gedacht, Code zwischen verschiedenen Plattformen zu teilen, indem sie als Ziel für das Ergebnis dienen können. Das sind zurzeit JVM-kompatible Umgebungen, JavaScript und in einer ersten Version auch iOS.
Der erste Punkt ist wichtig bei der Integration in ein Projekt, das eine klar definierte Zielplattform hat. Die Weiternutzung von Standard-Bibliotheken und Funktionen ist jeweils durch gegenseitige Funktionsaufrufe und Nutzung der Datenklassen möglich.
Bei beiden Einsatzszenarien sind es zwei wesentliche Punkte, an denen dieses Prinzip an seine Grenzen stößt: Inkompatible Sprachfeatures und plattformspezifische Sprachkomponenten.
In den folgenden Kapiteln beleuchte ich Grenzfälle von Interoperabilität auch, vor allem Kapitel 11 ist diesem Bereich gewidmet. In der Einführung möchte ich nur anhand von Beispielen für diese Thematik sensibilisieren.
Im ersten Fall, also bei inkompatiblen Sprachfeatures, kann es bei der Integration von Komponenten unter Umständen zu Komplikationen kommen, die einen umfangreichen Eingriff in den Code erfordern. Typsicherheit und Generics sind zwei Java-Beispiele für inkompatible Sprachfeatures, in denen Aufrufe aus Java bei der Kombination mit Kotlin-Code zu Compile-Fehlern führen können. So findet bei Zahlen in Kotlin keine automatische Erweiterung des Scopes statt, wie es bei Java üblich ist. Bei zwei verschiedenen Typen von Fließkommazahlen ist es in Java kein Problem, einer Funktion die ein double (eine 32-Bit Zahl) erwartet, ein float (gleiches Zahlenformat, aber nur 16 Bit) zu übergeben. Entweder stellt man mehrere Methoden bereit oder wandelt den Typ durch ein explizites Casting um.
Als Beispiel für plattformspezifische Komponenten kann der Zufallszahlengenerator dienen. Nutzt man die Klasse Random, um eine Zahl zu generieren, stützte sich Kotlin in vorherigen Versionen implizit auf java.util.Random. Ist das Ziel eine JavaScript-Anwendung, so würde es bei der Ausführung zu einem Laufzeitfehler kommen, daher erlaubt der Compiler keinen Einsatz von Methoden oder Feldern, die nicht zur Zielplattform passen. Umgekehrt gibt es Möglichkeiten, auf Klassen zum DOM-Parsing im Browser zuzugreifen. Diese existieren jedoch nur in einem JavaScript-Kontext und sind nicht für Android oder Kotlin als Java-Virtual-Machine kompatible Programme verfügbar.
In der Online-Dokumentation von Kotlin sind die entsprechenden Klassen oder Methoden mit Badges annotiert, die zeigen, für welche Umgebung eine Klasse zur Verfügung steht.
Das Feld currentTime
ist undefiniert, wenn wie oben Common oder JVM aktiviert sind (oberer Screen), da es nur in einer Java-Script-Umgebung zur Verfügung steht (unterer Screen).
Kapitel 2:
Arbeiten mit Kotlin
2.1 Entwicklungsumgebung einrichten
Um die ersten Schritte mit den Beispielen der kommenden Kapitel ausführen zu können, ist jetzt der Zeitpunkt, eine entsprechende Umgebung zu konfigurieren, um Kotlin auszuprobieren. Meine Empfehlung ist es, hierzu auf eine der frei verfügbaren Entwicklungsumgebungen zu setzen.
Da die ersten Schritte in einem neuen Tool eventuell nicht ganz selbsterklärend sind, stelle ich die beiden wichtigsten Umgebungen vor und zeige sie bis zum ersten minimalen funktionierenden Beispiel. Nun ändern sich Softwareversionen in der Regel schneller als Bücher, doch die grundlegenden Fragen der Organisation haben höchstwahrscheinlich noch eine Weile Bestand, auch wenn die Screens sich bis zur Drucklegung eventuell in Details schon wieder geändert haben.
Es besteht die Wahl zwischen zwei Varianten:
IntelliJ Community Edition: Für alle, deren Fokus nicht direkt auf Android liegt, oder die eventuell auch komplexere Cross-Plattform-Projekte ausprobieren möchten
Android Studio: Für diejenigen, deren Projekte auf Android beschränkt sind
2.1.1 IntelliJ
Nach der Auswahl Projekt > Neu stehen verschiedene Optionen für Kotlin-Projekte zur Verfügung.
Für die Beispielprojekte in den ersten Kapiteln reicht in der Regel das einfache Kotlin(Java)-Projekt aus. In den späteren Kapiteln wird dann auch die Variante für JavaScript, beziehungsweise ein Cross-Plattform-Projekt, verwendet. Wie an den Bezeichnern bereits ersichtlich ist, handelt es sich zum Teil um experimentelle Features. Diese sind nutzbar, es ist jedoch nicht garantiert, dass nach einem Update der Sprache oder der Tools alles noch läuft wie zuvor.
Im nächsten Schritt muss das Kind einen Namen bekommen. Wer Maven (ein anderes Tool zur Buildorganisation, vergleichbar mit Gradle) nutzt, wird die Einteilung in Artifact-ID, Group-ID und Version kennen. Die Group-ID kann leer bleiben, die Artifact-ID sollte den Projektnamen erhalten, und die Version kann auch zunächst auf dem Default-Wert stehen bleiben.
Die Projekte in IntelliJ werden standardmäßig mit Gradle konfiguriert und gebaut. Daher ist dieser Schritt wichtig. Im Normalfall sollte hier der Gradle-Wrapper aus der Basisinstallation gute Dienste tun.
Es kann im Einzelfall vorkommen, dass sich der aktuelle Wrapper nicht mit der Version von Plug-ins oder auch Java verträgt, in diesem Fall kann Gradle auch manuell installiert werden. Sollte das Tool Update-Bedarf anmelden, empfiehlt es sich in der Regel, diesem Rat zu folgen.
Falls sich Fehlermeldungen beim Erstellen des ersten Projekts zeigen, die anzeigen, dass Komponenten nicht gefunden werden oder der Gradle Sync nicht erfolgreich durchgeführt wird, empfiehlt sich ein Check der Version(en) von Java, IntelliJ, Kotlin-Plug-in für IntelliJ und Gradle-Wrapper. Sollte dieser nicht aktuell sein, ist ein Update möglich:
./gradlew wrapper --gradle-version 4.8.1 (aktuelle Version)
Mehr Informationen zu Gradle und Stand-Alone-Downloads gibt es unter folgender Adresse:
https://gradle.org/
Mit diesem leeren Projekt kann es im Prinzip losgehen.
Als Nächstes müssen noch grundlegende Verzeichnisse angelegt werden (oder die Option Neue Module mit leerem Source-Verzeichnis anlegen und in den Settings anhaken.)
Die Ordnerstruktur sollte folgenden Aufbau haben: src/main/kotlin.
Nun ist es an der Zeit, darin die erste Datei anzulegen. Passend zum Projekt soll sie den Namen firstrun.kt
tragen. Diese Datei benötigt ein wenig »overhead«, damit die Ausführung leicht möglich ist. Die Anweisungen machen die Kotlin-Anwendung kompatibel zur JVM. Das Ergebnis dieser Schritte sollte dann so aussehen:
Der letzte Schritt ist dann, eine Run-Configuration zu erstellen, damit der Play-Button zum Ausprobieren des Codes glühen kann.
Dazu wählen Sie in dem leeren Dropdown-Menü neben dem ausgegrauten Play-Button (vorheriger Screen rechts oben) den Punkt Edit configurations ... oder Konfigurationen bearbeiten .... Dort folgt mit dem Klick auf das Plus das Auswahlmenü für die Konfigurationstypen.
Hier wählen Sie Kotlin aus, um die grundlegenden Einstellungen für das erste Projekt zu öffnen (s. Abbildung 2.5)
Bei diesem letzten Schritt müssen Sie einmal die gerade erzeugte Klasse in dem hervorgehobenen Bereich Use classpath of Submodule auswählen.
Im folgenden Kapitel folgt Inhalt, mit dem die main
-Methode gefüllt und ausprobiert werden kann.
2.1.2 Android Studio
Mit dem Projektassistenten legen Sie auch in Android Studio eine leere Applikation an. Dieser Schritt generiert bereits ersten Kotlin-Code und ein leeres Layout, in dem Sie direkt arbeiten können und das auf dem Simulator oder dem eigenen Handy einen App Screen erzeugen sollte.
Im ersten Schritt scheint das Template Empty Activity zu passen, da es eine funktionierende Grundlage schafft, ohne viel Android-Infrastruktur zu erstellen, die in den ersten Schritten nicht notwendig ist. Wer sich mit Android auskennt, kann selbstverständlich auch mit einer Android TV App oder einer Fragment ViewModel-Variante beginnen.
Im nächsten Schritt legen Sie die Details fest, eventuell fällt hier auch erneute Downloadzeit für die richtige Android-Version an. Im Beispiel habe ich mich für eine recht alte API-Level entschieden, in einem Testprojekt kann das gerne aktueller sein. Der Name der App kann frei gewählt werden, der Package-Name muss den Konventionen für Java-Packages folgen und darf nur Kleinbuchstaben ohne Sonderzeichen enthalten.
Das Resultat ist eine Minimalapp im Code, die auch direkt zum Ausführen konfiguriert ist.
Die ersten Beispiele in diesem Buch gehen davon aus, dass der Code auf der Konsole ausgeführt wird. Es ist aber kein Problem, die Beispiele leicht anzupassen und sie dann entweder aus der Konsole von Android zu sehen oder aber in einem TextView
und damit auch direkt in einer App auszugeben.
Um die einfache Konsolenausgabe zu erreichen, die in den Codebeispielen mit print("Irgendwas")
gemacht wird, kann in einer Android-App System.out.print("Irgendwas")
verwendet werden – bzw. die Variante mit println
.
Um Ausgaben mit dem Textview
in einer App zu machen, muss dieser einmal in der Applikation aus der Layout-Datei gelesen werden (oder mittels kotlin-android-extensions direkt importiert) und kann dann mit textView.text
=
"Irgendwas"
genutzt werden, um die Ausgabe zu machen.
Führt man die App einmal aus und lässt sie sich anzeigen, so grüßen die Worte »Hello world« in einem ansonsten leeren App-Screen mit dem Titel Kotlin App.