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.6    Fortgeschrittene Themen Zur vorigen ÜberschriftZur nächsten Überschrift

In diesem Abschnitt stelle ich Ihnen fortgeschrittene Themen und Kotlin-Spezialitäten vor. Ich habe sie in diese Einführung gepackt, weil Sie beim Schreiben von Apps auf jeden Fall darauf stoßen werden.

 
Zum Seitenanfang

A.6.1    Singletons und Companion-Objekte Zur vorigen ÜberschriftZur nächsten Überschrift

Kotlin kennt im Gegensatz zu Java nicht das Schlüsselwort static. Variablen oder Funktionen ohne Bezug zu einer Klasse oder Instanz werden einfach auf Wurzel- oder Paketebene definiert, also in einer beliebigen .kt-Quelltextdatei (vor oder nach einer Klassendefinition). Aber wie geht man vor, wenn man auf private Daten oder Methoden einer Klasse zugreifen muss? Denken Sie an Zufallszahlengeneratoren. Die Funktion nextInt() könnte eigentlich prima ohne Klassen- oder Objektbezug funktionieren, aber wo wird dann der Zustand gehalten?

fun main() {
for (i in 1..3) {
println(NotSoRandom.nextInt())
}
}

object NotSoRandom {
private var a = 42

fun nextInt(): Int {
a *= 2
return a
}
}

Listing A.45    Singletons in Kotlin

Zugegeben, der Zustand des Singletons (eine Klasse mit genau einer Instanz) NotSoRandom in Listing A.45 ist überschaubar: eine Variable. Aber sie – und genau das möchte ich Ihnen mit dem Beispiel zeigen – kann sich bei mehreren Aufrufen ändern, ohne dass sich der Aufrufer einen Zustand merken und zum Beispiel als zusätzlichen Methodenparameter übergeben muss. Die Initialisierung von Singletons ist Thread-sicher. Sie werden wie Klassen definiert, nur verwenden Sie anstelle von class das Schlüsselwort object. Sie können dann einfach über den Namen auf Eigenschaften und Funktionen des Singletons zugreifen. Wenn es innerhalb einer Klasse verwendet wird, wirkt der benannte Zugriff aber unschön:

fun main() {
for (i in 1..6) {
println(NotSoRandom2().nextInt())
}
}

class NotSoRandom2 {
object Magic {
private val l = listOf(1, 2, 3)
private var pos = 0

fun next(): Int {
if (pos >= l.size)
pos = 0
return l[pos++]
}
}

fun nextInt() = Magic.next()
}

Listing A.46    Beispiel für unschönen benannten Zugriff

In Listing A.46 wird mit Magic.nextInt() die nächste (nicht zufällige) Zahl ermittelt. Da das Singleton Magic direkt in der Klasse NotSoRandom2 verwendet wird, stört die notwendige Nennung des Namens, zumal Kotlin-Code sonst sehr kompakt ist. Glücklicherweise lässt sich das Problem sehr leicht beheben. Machen Sie aus dem Singleton einfach ein sogenanntes Companion-Objekt, indem Sie vor object das Schlüsselwort companion setzen. Companion-Objekte können einen Namen haben, Magic kann also stehen bleiben. Sie können das Wort aber auch entfernen. In diesem Fall wäre ein benannter Zugriff mit dem Namen der umschließenden Klasse (NotSoRandom2) möglich. Methodenaufrufe – und das ist ja der Sinn davon – brauchen aber kein Präfix mehr, Magic. kann also entfallen.

[»]  Hinweis

Member des Companion-Objekts sind normale Instanz-Member der umschließenden Klasse. Soll das Programm in einer Java Virtual Machine ausgeführt werden, kann mit der Annotation @JvmStatic ein statischer Member erzeugt werden.

Klassen, Objekte, Interfaces, Konstruktoren, Funktionen, Eigenschaften und deren Setter können in ihrer Sichtbarkeit eingeschränkt werden. Welche Stufen es gibt und wie sie sich auswirken, zeige ich Ihnen im folgenden Abschnitt.

 
Zum Seitenanfang

A.6.2    Sichtbarkeit Zur vorigen ÜberschriftZur nächsten Überschrift

Kotlin kennt die Sichtbarkeitsstufen private, protected, internal und public. Wird kein Modifizierer angegeben, gilt public. Wenn Sie die Sichtbarkeit einschränken möchten, müssen Sie dies explizit angeben.

Schlüsselwort

Bedeutung

public

Zugriff nicht eingeschränkt

private

  • Private Toplevel-Deklarationen nur innerhalb der Datei sichtbar

  • Klassen, Interfaces und Properties nur innerhalb der Klasse bzw. des Interfaces

protected

  • Für Toplevel-Deklarationen nicht verfügbar

  • Klassen, Interfaces und Properties innerhalb der Klasse bzw. des Interfaces sowie in abgeleiteten Klassen

internal

  • Interne Toplevel-Deklarationen innerhalb eines Moduls sichtbar

  • Jeder innerhalb desselben Moduls, der die deklarierende Klasse sieht, sieht auch einen internen Member.

Tabelle A.3    Zugriffsmodifizierer und ihre Bedeutung

Module bestehen aus zusammen übersetzten Kotlin-Dateien. Mögliche Quellen sind IntelliJ IDEA-Module, Maven-Projekte, Gradle source sets sowie mehrere Quelltextdateien, die in einem <kotlinc> Ant Task übersetzt wurden.

 
Zum Seitenanfang

A.6.3    Erweiterungen Zur vorigen ÜberschriftZur nächsten Überschrift

Mit Erweiterungen können Sie Klassen um neue Funktionen erweitern, ohne von ihr abzuleiten oder auf klassische Entwurfsmuster (beispielsweise Decorator) zurückzugreifen. Das ist sehr praktisch, wenn der Quelltext einer Klasse nicht zur Verfügung steht, weil sie zu einer vorübersetzten Bibliothek gehört. Listing A.47 zeigt eine Erweiterung für Strings.

fun main() {
val s = "AB"
println(s.toAsterisk())
}

fun String.toAsterisk(): String {
return String(CharArray(this.length, { '*' }))
}

Listing A.47    Eine Erweiterungsfunktion für die Klasse »String«

this im Rumpf der Funktion ist optional. Erweiterungen werden statisch aufgelöst. Sie modifizieren Klassen nicht. Die neuen Funktionen werden nur über die Punkt-Notation aufrufbar. Der Receivertyp kann bei Bedarf nullbar oder generisch sein. Das ist in Listing A.48 zu sehen. Der Cast (this as Any) ist nötig, weil sonst die Ermittlung des Klassennamens nicht erlaubt ist.

fun main() {
"123".info()
123.info()
null.info()
}

fun <T> T?.info() {
println(
if (this == null)
"ist null"
else
(this as Any)::class.simpleName
)
}

Listing A.48    Erweiterungsfunktion mit nullbarem und generischem Receiver

Die zu erweiternde Klasse wird zur Compilezeit bestimmt. Abgeleitete Klassen werden deshalb anders behandelt, als man vielleicht erwarten würde. In Listing A.49 werden zwei Klassen definiert. B leitet von A ab. Beide erhalten eine Erweiterungsfunktion getName(). Diese liefern je nach Klasse "A" oder "B". Die Funktion printClassName() erhält ein Objekt des Typs A und ruft darauf die Erweiterungsfunktion getName() auf. Wenn eine B-Instanz übergeben wird, müsste demnach "B" zurückgegeben werden, oder?

fun main() {
printClassName(A())
printClassName(B())
}

open class A
class B: A()

fun A.getName() = "A"
fun B.getName() = "B"

fun printClassName(s: A) {
println(s.getName())
}

Listing A.49    Ermittlung der zu erweiternden Klasse zur Compilezeit

Der Typ des Receivers wird zur Compilezeit bestimmt. Das ist A, weshalb unabhängig vom Typ zur Laufzeit immer A.getName() aufgerufen wird.

 
Zum Seitenanfang

A.6.4    Kompakter Code Zur vorigen ÜberschriftZur nächsten Überschrift

Kotlin-Code soll kompakt, aber dennoch gut lesbar sein. Deshalb können Strukturelemente wie { } und ( ) unter bestimmten Umständen weggelassen werden. Beispielsweise würde man

(1..3).forEach( { i -> println(i * i)}) 

zu

(1..3).forEach { println(it * it) }

verkürzen.

Praktisch ist auch, dass Sie nicht verwendete Parameter in Lambda-Ausdrücken mit _ als wird nicht verwendet kennzeichnen können (Listing A.50).

var count = 0
(1..3).forEach( { _ -> count++ } )
println(count)

Listing A.50    Nicht verwendeter Parameter in einem Lambda-Ausdruck

Das wird üblicherweise noch weiter reduziert auf:

(1..3).forEach { count++ }

Es fällt also nicht nur der nicht verwendete Parameter weg, sondern auch der Lambdapfeil -> und die forEach-Funktionsklammern.

 


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