Rheinwerk Computing < openbook >

 
Inhaltsverzeichnis
Vorwort
Teil I Grundlagen
1 Android – eine offene, mobile Plattform
2 Hallo Android!
3 Von der Idee zur Veröffentlichung
Teil II Elementare Anwendungsbausteine
4 Wichtige Grundbausteine von Apps
5 Benutzeroberflächen
6 Multitasking
Teil III Gerätefunktionen nutzen
7 Telefonieren und surfen
8 Sensoren, GPS und Bluetooth
Teil IV Dateien und Datenbanken
9 Dateien lesen, schreiben und drucken
10 Datenbanken
Teil V Multimedia und Produktivität
11 Multimedia
12 Kontakte und Organizer
A Einführung in Kotlin
B Jetpack Compose
C Häufig benötigte Codebausteine
D Literaturverzeichnis
E Die Begleitmaterialien
Stichwortverzeichnis

Ihre Meinung?
Spacer
<< zurück
Android 11 von Thomas Künneth
Das Praxisbuch für App-Entwickler
Buch: Android 11

Android 11
Pfeil A Einführung in Kotlin
Pfeil A.1 Überblick
Pfeil A.1.1 Hello, Kotlin
Pfeil A.1.2 Beim Start Argumente übergeben
Pfeil A.2 Datentypen
Pfeil A.2.1 Zahlen
Pfeil A.2.2 Zeichen und Zeichenketten
Pfeil A.2.3 Wahrheitswerte
Pfeil A.2.4 Felder
Pfeil A.3 Programmlogik
Pfeil A.3.1 Funktionen
Pfeil A.3.2 Programmfluss
Pfeil A.3.3 Variablen und Eigenschaften
Pfeil A.4 Null-Sicherheit
Pfeil A.4.1 Nullbare Typen
Pfeil A.4.2 Elvis Operator und unsicherer Zugriff
Pfeil A.5 Objektorientierung
Pfeil A.5.1 Einfache Klassen
Pfeil A.5.2 Sekundäre Konstruktoren und init-Blöcke
Pfeil A.5.3 Gleichheit und Identität
Pfeil A.5.4 Vererbung
Pfeil A.6 Fortgeschrittene Themen
Pfeil A.6.1 Singletons und Companion-Objekte
Pfeil A.6.2 Sichtbarkeit
Pfeil A.6.3 Erweiterungen
Pfeil A.6.4 Kompakter Code
Pfeil A.7 Zusammenfassung
 
Zum Seitenanfang

A.3    Programmlogik Zur vorigen ÜberschriftZur nächsten Überschrift

In diesem Abschnitt sehen wir uns an, wie Sie Variablen und Datentypen in Ihren Programmen verwenden.

 
Zum Seitenanfang

A.3.1    Funktionen Zur vorigen ÜberschriftZur nächsten Überschrift

Anders als in Java oder C# können Sie in Kotlin Programme schreiben, ohne eine einzige Klasse zu definieren. Bei funktionalen Programmiersprachen spielen erwartungsgemäß Funktionen eine zentrale Rolle. Sie haben main() als Einstiegspunkt in ein Programm bereits kennengelernt. Den allgemeinen Aufbau einer Funktion zeigt Listing A.11. Vor fun können weitere Schlüsselwörter stehen, beispielsweise suspend (wird im Zusammenhang mit Koroutinen verwendet), private und internal (beide verändern die Sichtbarkeit der Funktion).

fun name(p1: Typ [= wert][, ...]): Typ {
...
return ...
}

Listing A.11    Struktur von Funktionen

Der Typ des Rückgabewerts wird nach einem Doppelpunkt angegeben. Fehlt die Typangabe, wird implizit Unit verwendet. Unit entspricht dem Schlüsselwort void in Java und wird verwendet, wenn eine Funktion keinen sinnvollen Wert zurückgibt. Anders als void ist Unit aber eine echte Klasse mit genau einer Instanz (Singleton). Darüber hinaus gibt es noch einen interessanten Spezialfall. Eine Funktion, die nie verlassen wird, weil sie eine gewollte Endlosschleife enthält oder die Verarbeitung mit throw abbricht, kann mit Nothing definieren, dass sie nichts zurückgibt (Listing A.12).

fun main() {
try {
a()
} catch (ex: Exception) {
println(ex.message)
}
}

fun a(): Nothing {
throw Exception("Nothing!")
}

Listing A.12    Verwendung des Typs »Nothing«

Nothing kann (natürlich) nicht instanziiert werden. Das ist mehr als reine Spielerei, denn wenn Sie versuchen, nach dem Aufruf von a() beispielsweise Text auszugeben, erhalten Sie die Warnung »Unreachable code«. Wie in vielen anderen Programmiersprachen werden Funktionen mit return verlassen. Je nach Rückgabetyp folgt dem Schlüsselwort ein dazu passender Wert. Im Falle von Unit entfällt dieser. Für formelartige Funktionen gibt es eine sehr kompakte Form ohne Funktionsrumpf:

fun name(p1: Typ [= wert][, ...]) = ...

Listing A.13 zeigt die Quadratfunktion in klassischer Schreibweise. In Listing A.14 sehen Sie denselben Code als kompakte Formel.

fun pow2(n: Int): Int {
return n * n
}

Listing A.13    Quadratfunktion in klassischer Schreibweise

fun pow2(n: Int) = n * n

Listing A.14    Quadratfunktion als einzeilige Formel

Kotlins Funktionen haben noch mehr zu bieten. Beispielsweise können Sie mit = ... Standardwerte vergeben. In diesem Fall muss der Wert beim Aufruf nicht übergeben werden – er ist optional. Je nach Position des optionalen Parameters müssen Sie aber für die anderen Argumente festlegen, für welche sie gelten. Solche benannten Parameter sind in Listing A.15 zu sehen.

fun main() {
f()
f(1, 2)
f(p2 = 42)
}

fun f(p1: Int = 2, p2: Int = 1) {
println("p1 = $p1, p2 = $p2")
}

Listing A.15    Benannte und optionale Funktionsparameter

Beim Aufruf f(1, 2) werden alle Parameter übergeben. Eine Benennung ist deshalb nicht nötig. f(42) ließe aber offen, ob p1 oder p2 gemeint ist. Deshalb muss hier die zu füllende Variable angegeben werden: f(p2 = 42).

 
Zum Seitenanfang

A.3.2    Programmfluss Zur vorigen ÜberschriftZur nächsten Überschrift

Mit Funktionen allein können Sie schon viele Programmierprobleme lösen. Um komplexe Abläufe bequem umsetzen zu können, sind aber Kontrollstrukturen (Schleifen sowie Verzweigungen mit und ohne Bedingungen) hilfreich. Diese sehen wir uns im Folgenden an.

Schleifen

Java, JavaScript und C# erben ihre flexiblen for-Schleifen von C. Außer der obligatorischen Abbruchbedingung gibt es dort die jeweils optionale Initialisierung und Inkrementierung. Der klassische Anwendungsfall, das Iterieren über einen Bereich, lässt sich in Kotlin ähnlich flexibel, aber in kompakterer Form darstellen. Listing A.16 gibt die Zahlen 1, 2 und 3 in aufsteigender Reihenfolge aus.

for (i in 1..3) {
println(i)
}

Listing A.16    Die Zahlen von 1 bis 3 in aufsteigender Reihenfolge ausgeben

Um die Schleifenvariable bei jedem Durchlauf zu verkleinern, wird downTo verwendet. step gibt die Schrittweite an. Listing A.17 gibt die Zahlen 6, 4, 2 und 0 in absteigender Reihenfolge aus.

for (i in 6 downTo -1 step 2) {
println(i)
}

Listing A.17    Die Zahlen 6, 4, 2, 0 ausgeben

Obwohl als unteres Intervallende -1 angegeben wurde, wird dieser Wert nicht ausgegeben, weil step 2 die Schleifenvariable i ausgehend vom Startwert 6 immer um 2 vermindert. Möchten Sie das Intervallende nicht berücksichtigen, ist es oftmals einfacher, until zu verwenden. Listing A.18 gibt die Zahlen 1 und 2 in dieser Reihenfolge aus.

for (i in 1 until 3) {
println(i)
}

Listing A.18    Ausgabe der Zahlen 1 und 2

In vielen Programmiersprachen gibt es das Muster, in Schleifen auf Array-Elemente zuzugreifen. Im folgenden Beispiel liefert array.indices einen Bereich, der alle Indizes des Felds enthält, von 0 bis zur Anzahl der Elemente –1. Über diesen kann dann mit for iteriert werden.

val array = arrayOf("Eins", "Zwei", "Drei")
for (i in array.indices) {
println(array[i])
}

Listing A.19    Über die Elemente eines Arrays iterieren

Einfacher ist, direkt Listen zu durchlaufen (Listing A.20). Die Ausgabe entspricht der von Listing A.19.

val list = listOf("Eins", "Zwei", "Drei")
for (i in list) {
println(i)
}

Listing A.20    Über eine Liste iterieren

Noch kompakter wird der Code, wenn Sie die Methode forEach() einer Liste aufrufen.

list.forEach( {i -> println(i) } )

Während for über Bereiche iteriert, führt while einen Codeblock so lange aus, wie die Schleifenbedingung erfüllt ist. Listing A.21 simuliert eine for-Schleife mit while. Unschön an diesem Beispiel ist, dass die Laufvariable explizit deklariert und initialisiert werden muss. Hier sollten Sie stattdessen for verwenden.

var i = 0
while (i++ < 3) {
println(i)
}

Listing A.21    Nachbildung der for-Schleife mit while

while bietet sich an, wenn die Schleifenbedingung das Ergebnis eines Funktionsaufrufs prüft. In Listing A.22 wird so lange ein Punkt ausgegeben, bis eine Zufallszahl zwischen 0 und 99 der Zahl 42 entspricht.

while ((Math.random() * 100).toInt() != 42) {
print(".")
}

Listing A.22    while-Schleife ohne Laufvariable

for und while arbeiten mit Bedingungen. Darüber hinaus kennen die meisten Programmiersprachen das Schlüsselwort if, um den Programmfluss zu steuern. Natürlich auch Kotlin. Diese und weitere bedingte Verzweigungen sehen wir uns im folgenden Abschnitt an.

Bedingungen

Im Quelltext folgt auf if stets ein Ausdruck. Ist dieser im Sinne der Datentypen einer Programmiersprache wahr, wird die darauffolgende Anweisung bzw. Block abgearbeitet. Falls nicht, kommt die dem Schlüsselwort else folgende Anweisung bzw. der Block zum Zuge. Der else-Zweig ist üblicherweise optional. Listing A.23 zeigt die grundsätzliche Vorgehensweise in Kotlin. % ist der Modulo-Operator. Sollen mehrere Anweisungen ausgeführt werden, müssen diese mit { } geklammert werden.

fun main() {
val num = (Math.random() * 4).toInt()
if (isEven(num))
println("$num ist gerade")
else
println("$num ist ungerade")
}

fun isEven(n: Int): Boolean = n % 2 == 0

Listing A.23    »if« als Anweisung

In Kotlin kann if aber auch als Ausdruck verwendet werden. Dessen Ergebnis ist das Ergebnis des letzten Ausdrucks im jeweiligen Zweig. Wie das funktioniert, zeigt Listing A.24.

fun main() {
val num = (Math.random() * 4).toInt()
println("""$num ist ${
if (isEven(num))

"gerade"
else
"ungerade"
}""")
}
...

Listing A.24    »if« als Ausdruck

Mit """ wird ein Raw String eingeleitet, deshalb sind ohne Probleme Zeilenumbrüche möglich. ${ ... } sorgt dafür, dass das Ergebnis des if-Ausdrucks ein Bestandteil des Raw Strings wird. Für gerade Zahlen ist dies »gerade«, für ungerade der Text »ungerade«.

Auswahl mit »when«

Die in vielen Programmiersprachen vorhandenen switch-case-Konstrukte werden in Kotlin mit when abgebildet. Listing A.25 zeigt einen einfachen Anwendungsfall. Zunächst wird mit (0..3) ein Bereich (IntRange) definiert und mit shuffled() in eine Liste umgewandelt. first() liefert das erste Element der Liste. Ist es 0, wird der String »0« ausgegeben, bei 2 »2«. Alle anderen Werte (1 oder 3) führen zur Ausgabe von »1 oder 3«.

val num = (0..3).shuffled().first()
println(num)
when (num) {
0 -> println("0")
2 -> println("2")
else -> println("1 oder 3")
}

Listing A.25    Ein einfaches Beispiel mit »when«

when prüft das Argument der Reihe nach auf Übereinstimmung mit den Bedingungen der Zweige. Hier sind auch mehrere Werte je Zweig zulässig. Anstelle von else könnten Sie deshalb 1, 3 schreiben. when kann wie if als Ausdruck verwendet werden. Allerdings ist dann der else-Zweig obligatorisch, es sei denn, der Compiler kann beweisen, dass die übrigen Zweige alle möglichen Werte abdecken.

 
Zum Seitenanfang

A.3.3    Variablen und Eigenschaften Zur vorigen ÜberschriftZur nächsten Überschrift

Bei der Verwendung des Begriffs Variable muss man eigentlich genau definieren, welche Art gemeint ist, zum Beispiel lokale, statische oder Instanzvariable. In Kotlin existieren Variablen in dem Bereich oder Codeblock, in dem sie deklariert wurden, beispielsweise in der Signatur einer Funktion oder im Funktionsrumpf. Sie werden mit = gesetzt und gelesen.

Darüber hinaus gibt es, wie beispielsweise in C#, Eigenschaften. Auch auf diese greifen Sie mit = zu. Allerdings haben Sie viel mehr Einfluss darauf, wie Werte gelesen und geschrieben werden. Java kennt (leider) keine Eigenschaften, sie werden mit get...()-, is...()- und set...()-Methoden simuliert. Androids Klassenbibliothek macht exzessiv von solchen Getter- und Setter-Methoden Gebrauch. In Kotlin sind Eigenschaften mit Getter und Setter fester Bestandteil der Sprache. Deshalb schlägt Android Studio bei der Eingabe von Code auch immer vor, Zugriffe mit = zu machen. textView.text = getString(R.string.name) ist also besser als textView.setText(getString(R.string. name)). Dumm nur, dass sie in der API-Doku nichts über text finden, sondern nach setText() oder getText() suchen müssen.

var a: Int = 0
get() = field
set(value) {
field = value
}

fun main() {
a = 42
println(a)
}

Listing A.26    Eigenschaften mit Getter und Setter

Listing A.26 zeigt, wie Sie Eigenschaften definieren. field ist eine von Kotlin automatisch zur Verfügung gestellte Variable. Sie übernimmt die Speicherung des Werts. Falls der Getter das Ergebnis eines Funktionsaufrufs liefert, ist das aber nicht nötig. Nach val ist set() ungültig, darf also nicht angegeben werden.

Späte Initialisierung mit »lateinit«

Eigenschaften können, sofern sie zur Laufzeit nicht als primitive Datentypen dargestellt werden, bei erstmaligem Gebrauch initialisiert werden. Hierfür wird das Schlüsselwort lateinit (Listing A.27) verwendet.

lateinit var a: String

fun main() {
if (! ::a.isInitialized)
a = "Hallo, Kotlin"
println(a)
}

Listing A.27    Späte Initialisierung mit »lateinit«

Wenn Sie eine lateinit-Eigenschaft vor dem Setzen des Wertes auslesen, wird zur Laufzeit eine UninitializedPropertyAccessException (»lateinit property a has not been initialized«) geworfen. Deshalb können Sie bei Bedarf mit isInitialized prüfen, ob die Eigenschaft schon initialisiert wurde.

Konstanten

Steht bei der erstmaligen Zuweisung der Wert zur Compilezeit fest, können Sie die betreffende Eigenschaft mit dem Schlüsselwort const als (echte) Konstante kennzeichnen. Allerdings ist das nur außerhalb von Klassen und innerhalb von object zulässig (Listing A.28). Zur Laufzeit berechnete Werte (beispielsweise Ergebnisse von Funktionsaufrufen) sind nicht zulässig.

const val a = 10

object C {
const val c = 42
}

fun main() {
println(C.c)
}

Listing A.28    Verwendung von Konstanten

Wo Sie eine Konstante oder eine wie eine Konstante verwendete Variable definieren, hat Auswirkung auf die Regeln zu Groß- und Kleinschreibung. Unter Android ist es bewährte Praxis, bei Logausgaben ein sogenanntes Tag zu übergeben, eine Zeichenkette mit dem Namen TAG. Die Großschreibung gehört zum Java-Erbe von Android. Um dieser Konvention auch in Kotlin folgen zu können, muss sie folgendermaßen definiert werden:

private val TAG = SubscriptionManagerDemoActivity::class.simpleName

Also außerhalb der Klasse SubscriptionManagerDemoActivity oder in einem companion Objekt. Denn innerhalb einer Klasse folgen Variablennamen dem camelCase-Muster und beginnen mit einem Kleinbuchstaben.

 


Ihre Meinung?

Wie hat Ihnen das Openbook gefallen? Wir freuen uns immer über Ihre Rückmeldung. Schreiben Sie uns gerne Ihr Feedback als E-Mail an kommunikation@rheinwerk-verlag.de

<< zurück
Zur Rheinwerk-Konferenz für Kotlin
 Buchempfehlungen
Zum Rheinwerk-Shop: Kotlin

Kotlin


Zum Rheinwerk-Shop: Praxisbuch Usability und UX

Praxisbuch Usability und UX


Zum Rheinwerk-Shop: Flutter und Dart

Flutter und Dart


Zum Rheinwerk-Shop: App-Design

App-Design


 Lieferung
Versandkostenfrei bestellen in Deutschland, Österreich und in die Schweiz
InfoInfo

 
 


Copyright © Rheinwerk Verlag GmbH 2023
Für Ihren privaten Gebrauch dürfen Sie die Online-Version natürlich ausdrucken. Ansonsten unterliegt das Openbook denselben Bestimmungen, wie die gebundene Ausgabe: Das Werk einschließlich aller seiner Teile ist urheberrechtlich geschützt.
Alle Rechte vorbehalten einschließlich der Vervielfältigung, Übersetzung, Mikroverfilmung sowie Einspeicherung und Verarbeitung in elektronischen Systemen.

 
[Rheinwerk Computing]

Rheinwerk Verlag GmbH, Rheinwerkallee 4, 53227 Bonn, Tel.: 0228.42150.0, Fax 0228.42150.77, service@rheinwerk-verlag.de

Cookie-Einstellungen ändern