Hier ist eine Reihe von Themenbereichen aufgelistet, die du einzeln auf- und zuklappen kannst (Klick auf die kleine Pfeilspitze).
Die wichtigsten Informationen sind schwarz geschrieben.
Zusatz- oder Hintergrundinformationen sind grau geschrieben.
Wenn ich eine Funktion definiere, programmiere ich aus Einzelteilen einen Baustein, den ich später (mithilfe des von mir festgelegten Funktionsbezeichners) als Ganzen verwenden kann. In Kotlin gibt es dafür zwei Möglichkeiten.
Eine so notierte Funktion berechnet immer anhand des Funktionsterms genau einen Rückgabewert.
Allgemein: | Beispiel: |
|---|---|
|
|
Die Parameter aus der Parameterliste sind nur "innerhalb der jeweiligen Funktion", hier also rechts vom =-Zeichen der jeweiligen Funktionsdefinition, gültig.
Beachte, dass – anders als bei Klassen – vor den Parametern nicht val oder var steht.
Eine so notierte Funktion kann einen Rückgabewert erzeugen oder auch nicht. Sie kann außerdem noch allerhand anderes erledigen.
Allgemein: | Beispiele: |
|---|---|
|
|
Wenn – mithilfe der return-Anweisung – ein Rückgabewert erzeugt wird, muss dessen Datentyp im Anschluss an die Parameterliste angegeben werden.
Die Parameter aus der Parameterliste sind nur "innerhalb der jeweiligen Funktion", hier also innerhalb der geschweiften Klammern der jeweiligen Funktionsdefinition, gültig.
Nachdem eine Funktion definiert worden ist, kann ich sie in einem Term (allein oder in Kombination) verwenden. Wir sprechen auch davon, dass ich eine Funktion "aufrufe". Dabei benutze ich den Bezeichner der Funktion, gefolgt von der Parameterliste, in der jetzt für jeden Parameter ein passender Übergabewert stehen muss.
Funktionsdefinition: | Verwendung/Aufruf der Funktion: |
|---|---|
|
|
Wenn die Funktion einen Rückgabewert erzeugt, hat der Funktionsaufruf im Term den Wert des Rückgabewerts.
Term | Wert des Terms |
|---|---|
|
|
|
|
Eine Variable ermöglicht es, Werte im Arbeitsspeicher zu speichern, um sie zu einem späteren Zeitpunkt wieder abzurufen und damit weiterzuarbeiten. Mit einer Variablen kann ich das über einen von mir selbst festgelegten Bezeichner tun.
So muss ich mich nicht darum kümmern, wie ein geeigneter Speicherplatz gefunden werden kann, über welche Speicheradresse (eine Zahl) darauf zugegriffen werden muss, wie sichergestellt wird, dass kein anderes Programm mir meine Werte überschreibt usw.
Allgemein: | Beispiel: |
|---|---|
|
|
|
|
Benutze ich das Schlüsselwort var, kann ich den Wert der Variablen später neu setzen. Benutze ich stattdessen das Schlüsselwort val, so lässt sich der Wert später nicht mehr ändern.
Wenn ich mir nicht sicher bin, kann ich einfach var benutzen, dann kann in der Regel nichts schiefgehen.
Allgemein: | Beispiel: |
|---|---|
|
|
|
|
Der Datentyp kann weggelassen werden, wenn das System ihn am Wert erkennen kann, der der Variable zugewiesen wird.
null erlaubenWenn es möglich sein soll, dass die Variable überhaupt keinen sinnvollen Wert hat, wenn ich also den Platzhalterwert null als Wert der Variablen zulassen möchte, muss ich die Langform benutzen – und hinter den Datentyp ein Fragezeichen setzen (siehe VIII.)
Beispiel: |
|---|
|
|
Wenn ich einen Variablenbezeichner in einem Term benutze, wird er durch den aktuellen Wert der Variable ersetzt.
Schreibe ich aber nach einem Variablenbezeichner ein =-Zeichen, so muss darauf ein Term folgen. Der Wert dieses Terms wird der Variable dann als neuer Wert zugewiesen. Das geht nur, wenn mit Schlüsselwort var deklariert wurde. Außerdem muss der Datentyp, den der Wert des Terms hat, mit dem Datentyp der Variable übereinstimmen.
Deklaration & Initialisierung: | Verwendung der Variable: |
|---|---|
|
|
|
|
Ein Algorithmus ist eine endliche Folge eindeutiger Anweisungen, die, wenn sie unter gleichen Ausgangsbedingungen ausgeführt wird, zum gleichen Ergebnis führt. Wenn ein Algorithmus in einer Programmiersprache formuliert wird, entsteht ein Programm. Die jeweilige Programmiersprache legt fest, was als eindeutige Anweisung gilt. In Kotlin sind das Definitionen von Variablen, Wertzuweisungen an Variablen, Funktionsaufrufe, Methodenaufrufe ...
Wenn mehrere Anweisungen als zusammengehörig gekennzeichnet werden müssen, werden sie in geschweifte Klammern {...} eingeschlossen. Gebraucht wird das bei Funktionsdefinitionen, bedingten Anweisungen, bedingten Wiederholungen ...
Bedingte Anweisungen, bedingte Wiederholungen etc. können selbst auch innerhalb eines solchen Blocks vorkommen, dann gibt es z. B. innerhalb der geschweiften Klammern nach einem if wieder ein if {...}.
Variablen, die innerhalb eines Blocks deklariert werden, gelten in diesem Block – und in allen darin enthaltenen Blöcken -, aber nicht außerhalb.
Unter der gegebenen Bedingung etwas Bestimmtes machen, ansonsten gar nichts machen:
Allgemein: | Beispiel: |
|---|---|
|
|
Unter der gegebenen Bedingung etwas Bestimmtes machen, ansonsten etwas anderes machen:
Allgemein: | Beispiel: |
|---|---|
|
|
Wenn mehr als zwei Fälle unterschieden werden sollen, kann nach else direkt mit if (...) {...} weitergemacht werden usw.
Wenn die angegebene Bedingung erfüllt ist, etwas Bestimmtes machen, danach wieder von vorn prüfen, ob die Bedingung erfüllt ist, und wenn ja, dasselbe wieder machen und immer so weiter, bis die Bedingung nicht mehr erfüllt ist.
Wenn die Bedingung bei der ersten Prüfung nicht erfüllt ist, wird gar nichts gemacht (wie bei if).
Allgemein: | Beispiel: |
|---|---|
|
|
Objekte sind Bündel von Variablen bzw. Funktionen, die unter einem einzigen Bezeichner abgespeichert werden können. Die Variablen im Bündel nennen wir Attribute, die Funktionen im Bündel nennen wir Methoden.
Solange es noch einen Bezeichner gibt, unter dem ein Objekt gespeichert ist, bleibt es – mit allen seinen Attributen und Methoden – erhalten. Gibt es keinen solchen Bezeichner mehr, wird das Objekt entsorgt.
Um Objekte zu erzeugen, programmiere ich üblicherweise eine Klasse, in der festgelegt ist, wie Objekte eines bestimmten Typs aussehen sollen. Die Klasse kann dann beliebig viele Objekte ihres Typs erzeugen. "Ihres Typs" ist hier in jeder Hinsicht wörtlich zunehmen: Mit jeder Klasse erzeuge ich auch einen neuen Datentyp. Dieser Datentyp hat denselben Bezeichner wie die Klasse.
Datenklassen legen nur Attribute, jedoch keine Methoden fest.
Allgemein: | Beispiel: |
|---|---|
|
|
|
|
Die Auflistung der Attribute, die in Klammern auf den Klassenbezeichner folgt, nennen wir Konstruktor. Ob der Konstruktor in einer Zeile steht oder auf mehrere Zeilen aufgeteilt wird, spielt keine Rolle.
Der Konstruktor ähnelt der Parameterliste einer Funktion (und er wird auch genau so verwendet – siehe unten). Anders als vor gewöhnlichen Parametern muss aber vor jedem Attributbezeichner val oder var stehen. Das kennen wir von der Deklaration von Variablen. (Auf eine Initialisierung verzichten wir an dieser Stelle in der Regel – möglich wäre sie aber.)
Wenn ich eine Klasse als data class definiere, bekommt sie automatisch ein paar praktische Methoden zum Ausgeben und Vergleichen der Daten, ohne dass ich diese Methoden eigens festlegen müsste (siehe unten).
Klassendefinition: | Erzeugung von Objekten: |
|---|---|
|
|
|
|
Typischerweise werden neu erzeugte Objekte (wie hier in den Beispielen) direkt einer (neuen oder bereits existierenden) Variablen als Wert zugewiesen. Das muss aber nicht sein: Was in den Beispielen hinter dem =-Zeichen steht, könnte z. B. auch in einem Funktionsaufruf als Übergabewert verwendet werden.
Mithilfe der Punktnotation kann ich auf die Attribute von Datenobjekten zugreifen. Ich schreibe vor dem Punkt einen Objektbezeichner (also den Bezeichner einer Variable, als deren Wert ein Objekt gespeichert ist) und nach dem Punkt einen Attributbezeichner (jeweils ohne Leerzeichen dazwischen). Zusammen lässt sich dies genau so verwenden wie ein einfacher Variablenbezeichner.
Die folgenden Beispiele setzen die Beispiele unter IV.1.b) voraus:
Term | Wert des Terms |
|---|---|
|
|
|
|
|
|
Wertzuweisung | Wert des Attributs danach |
|---|---|
|
|
|
|
Außer Attributen können in einer Klasse auch Methoden festgelegt werden. Dies geschieht in einem Block, der nach dem Konstruktor folgt, dem sogenannten Klassenrumpf.
Allgemein: | Beispiel: |
|---|---|
|
|
Im Klassenrumpf können auch (weitere) Attribute deklariert werden – die müssen dann auch gleich initialisiert werden (normalerweise mit einem festen Wert, der dann für alle Objekte der Klasse gleich ist).
Allgemein: | Beispiel: |
|---|---|
|
|
Wenn gleich bei der Erzeugung eines Objekts bestimmte Aufgaben erfüllt werden sollen, werden diese in einem init-Block angegeben.
Allgemein: | Beispiel: |
|---|---|
|
|
Das Erzeugen "vollständiger" Objekte geht genauso wie das Erzeugen von Datenobjekten. Um Methoden aufzurufen, verwende ich genauso wie beim Zugriff auf Attribute die Punktnotation (siehe oben).
Klassendefinition: | Erzeugung und Verwendung von Objekten: |
|---|---|
|
|
Jedes Objekt hat die Kontrolle darüber, was mit seinen – in den Attributen gespeicherten – Daten gemacht wird. Methoden des Objekts können dazu immer ohne Punktnotation auf die Attribute des Objekts zugreifen. Andere Programmteile können dann mit Punktnotation auf die Attribute zugreifen, wenn das nicht verboten wurde: Um das zu verbieten, kann man in einer Klassendefinition das Schlüsselwort private (oder das Schlüsselwort protected) vor die Deklaration des Attributs schreiben.
Klassendefinition: | Erzeugung und Verwendung von Objekten: |
|---|---|
|
|
Eine Unterklasse "erbt" alle Attribute und Methoden ihrer Oberklasse. Üblicherweise "erweitert" sie diese durch weitere Attribute bzw. Methoden. Oder sie "überschreibt" bestimmte Methoden, die sie geerbt hat.
Eine Klasse, die als Oberklasse dienen soll, wird so definiert, wie oben beschrieben, vor dem Schlüsselwort class muss aber das Schlüsselwort open stehen. Wenn es möglich sein soll, eine Methode der Klasse zu überschreiben, muss auch bei der Definition der entsprechenden Methode das Schlüsselwort open verwendet werden.
Beispiel: |
|---|
|
Wenn eine Klasse nur als Oberklasse dienen soll, selbst aber keine Objekte erzeugen können soll, kommt das Schlüsselwort abstract zum Einsatz – auch bei der Deklaration von Methoden, die noch gar nicht definiert werden, die es aber in Unterklassen auf jeden Fall geben muss:
Beispiel: |
|---|
|
Eine Unterklasse erbt alles, was die Oberklasse kennzeichnet. Objekte, die von ihr erzeugt werden, werden sich (auch) so verhalten, wie man es von Objekten der Oberklasse erwartet – deshalb haben sie (auch) den Datentyp der Oberklasse. Dementsprechend setzt man hinter den Konstruktor einer Unterklasse einen Doppelpunkt (:); danach folgt (wie sonst ein Datentyp) der Bezeichner der Oberklasse, mit der Liste von Übergabewerten, die der Konstruktor der Oberklasse fordert.
Das folgende Beispiel setzt das Beispiel unter V.1.a) voraus:
|
Beachte im Beispiel oben:
override definiert werden.kantenlaenge ist in diesem Beispiel kein Attribut der Unterklasse, sondern ein gewöhnlicher Parameter (wie in einer Funktion), der nur dazu dient, die Werte der geerbten Attribute laenge und breite zu setzen.Das nächste Beispiel setzt das Beispiel unter V.1.b) voraus:
|
Beachte im Beispiel oben:
kantenlaenge, aber mX und mY sind gewöhnliche Parameter – wie im vorigen Beispiel.verschieben-Methode wird ganz normal geerbt.Zum Erzeugen von Objekten einer Unterklasse gibt es nichts Besonderes anzumerken.
Hilfsvorstellung: Ein Array ist wie ein benanntes Regal mit durchnummerierten Regalfächern. In jedem Fach kann ein Objekt abgelegt werden.
Arrays erlauben es, mehrere Werte desselben Datentyps an durchnummerierten Speicherplätzen unter einem einzigen Variablenbezeichner zu speichern. Mit der Kombination aus diesem Variablenbezeichner und einer Speicherplatznummer – dem sogenannten Index – kann auf die einzelnen Werte zugegriffen werden. Dabei kann der Index auch mithilfe einer Variable (vom Typ Int) angegeben werden. So ist es möglich, ein Programm (oder die Nutzerin des Programms) steuern zu lassen, auf welchen Wert zugegriffen werden soll.
arrayOfNulls-FunktionDie arrayOfNulls-Funktion erzeugt ein "leeres" Array für Objekte des gewünschten Datentyps. "Leer" ist das Array insofern, als an jedem Speicherplatz das null-Objekt gespeichert wird.
Allgemein: | Beispiel: |
|---|---|
|
|
Die Datentypen der in den Beispielen erzeugten Arrays sind Int? und String?.
arrayOf-FunktionDie arrayOf-Funktion wird mit einer beliebigen Zahl von Übergabewerten eines beliebigen Datentyps aufgerufen. Sie erzeugt ein Array, in dem genau die übergebenen Werte in der angegebenen Reihenfolge gespeichert sind.
Allgemein: | Beispiel: |
|---|---|
|
|
ArrayDem Konstruktor der Klasse Array wird ein ganzzahliger Übergabewert für die gewünschte Größe des Arrays übergeben und zusätzlich eine Funktion, die die Startwerte für die Speicherplätze im Array berechnet.
Allgemein: | Beispiel: |
|---|---|
|
|
oder | |
|
|
Um auf einen bestimmten Wert zuzugreifen, der in einem Array gespeichert ist, schreibe ich
bezeichner[index].
Deklaration & Initialisierung: | Verwendung des Arrays: |
|---|---|
|
|
Um flexibel auf verschiedene Werte im Array zugreifen zu können, gebe ich den Index mithilfe einer Variable an. Den Wert der Variable verändere ich im Rahmen einer bedingten Wiederholung so, dass jeder gewünschte Index einmal 'an die Reihe kommt'.
Deklaration & Initialisierung: | Iterieren über das Array: |
|---|---|
|
|
whenAllgemein: | Beispiel: |
|---|---|
|
|
Die Auflistung der Bedingungen wird von oben nach unten abgearbeitet. Wenn eine der Bedingungen erfüllt ist, werden die folgenden Bedingungen nicht mehr geprüft – es wird also immer nur eine der Anweisungen auf der rechten Seite der "Pfeile" ausgeführt.
Den Wert eines Terms mit verschiedenen Mustern vergleichen und jeweils entsprechend reagieren.
Allgemein: | Beispiel: |
|---|---|
|
|
Auf der linken Seite eines "Pfeils" können auch, durch Komma getrennt, mehrere Muster angegeben werden.
forEiner Variablen der Reihe nach jeden Wert aus einem Bereich von ganzzahligen Werten zuweisen – und demnach so oft wiederholen, wie es die Anzahl von Werten im Bereich vorgibt.
Allgemein: | Beispiel: |
|---|---|
|
|
Beachte, dass in der Klammer eine Variable (im Beispiel zahl) deklariert wird, ohne dass dafür das Schlüsselwort var verwendet wird. Diese Variable kann, wie im Beispiel, in den zu wiederholenden Anweisungen verwendet werden (das muss aber nicht sein, dann liegt eine "klassische Wiederholung mit fester Anzahl" vor).
Das lässt sich auch zum Iterieren über ein Array verwenden:
Deklaration & Initialisierung: | Iterieren über das Array: |
|---|---|
|
|
Vorsicht bei Verwendung mit Listen etc.: Wenn deren Größe im Rahmen der Wiederholung verändert wird, geschehen ggf. merkwürdige Dinge ...
Auch praktisch: Im Beispiel oben könnte index in 0..zahlen.count()-1 durch index in zahlen.indices ersetzt werden. Warum das geht, erklärt sich aus dem Folgenden ...
Einer Variablen der Reihe nach jeden Wert aus einer Collection (also einem Array, einer Liste o. Ä.) zuweisen.
Allgemein: | Beispiel: |
|---|---|
|
|
Hier wird die in der Klammer deklarierte Variable auf jeden Fall in den zu wiederholenden Anweisungen verwendet werden.
Achtung: Wenn ich for so verwende, kann ich mithilfe der in der Klammer deklarierten Variable keine Werte in die Collection schreiben. Es darf auch im Rahmen der Wiederholung gar nichts an der Collection verändert werden!
null-Sicherheitnull zulassenManchmal ist es nötig, "in" einer Variablen anstatt eines Wertes die Information zu speichern, dass es momentan keinen sinnvollen Wert für die Variable gibt. Dazu dient das null-Objekt mit dem Bezeichner null. In Kotlin kann dieses Objekt nur dann als Wert einer Variablen verwendet werden, wenn diese Möglichkeit ausdrücklich zugelassen wurde: Dazu muss man, wenn man eine Variable deklariert, ihren Datentyp ausdrücklich angeben und hinter das letzte Zeichen des Datentyps ein Fragezeichen setzen (siehe II.1.c)).
Deklaration & Initialisierung: | Verwendung: |
|---|---|
|
|
Wenn nun aber der Bezeichner einer solchen Variablen in Punktnotation verwendet wird, um eine Methode des entsprechenden Objekts aufzurufen, erzeugt das möglicherweise einen Fehler (die berühmt-berüchtigte NullPointerException) und führt zum Programm-Abbruch: Denn vom null-Objekt kann man keine Methoden aufrufen.
null-Objekt vermeidenWenn der Bezeichner einer Variable, deren Wert null sein könnte, in Punktnotation verwendet wird, muss ein Fragezeichen an den Variablenbezeichner angeschlossen werden. Die nach dem Punkt folgende Nachricht (Methodenaufruf) wird dann nur gesendet, wenn nicht gerade das null-Objekt als Variablenwert gespeichert ist.
Wenn auf diese Weise eine Methode aufgerufen werden soll, die einen Rückgabewert liefert, wird ein eventuell nicht ermittelbarer Rückgabewert (falls eben gar keine Methode aufgerufen wird) seinerseits durch null ersetzt.
Deklaration & Initialisierung: | Verwendung: |
|---|---|
|
|
null-Rückgabewerte abfangenEin null-Rückgabewert kann mithilfe des sogenannten Elvis-Operators ?: durch einen anderen ersetzt werden: Man setzt diesen Operator hinter den Term, dessen Wert null sein könnte, und schließt dann einen sinnvollen Wert vom eigentlich erwarteten Datentyp (oder einen Term, aus dem sich der Wert errechnet) an.
Deklaration & Initialisierung: | Verwendung mit Elvis-Operator: |
|---|---|
|
|
Operator: | Bedeutung: |
|---|---|
| plus |
| minus |
| mal |
| geteilt durch (ggf. ganzzahlig ohne Rest) |
| modulo (ergibt den Rest der ganzzahligen Division) |
Operator: | Bedeutung: |
|---|---|
| weist den Wert des Terms rechts vom Operator der Variable mit dem Bezeichner links vom Operator zu! (Wertzuweisung) |
Aussagefunktionen sind Funktionen, deren Rückgabewert entweder true oder false ist.
Operator: | Bedeutung: |
|---|---|
| ist gleich |
| ist nicht gleich |
| ist größer als |
| ist kleiner als |
| ist größer als oder gleich |
| ist kleiner als oder gleich |
Operator: | Bedeutung: |
|---|---|
| UND |
| ODER |
| NICHT |
Operator: | Bedeutung: |
|---|---|
bezeichner | prüft, ob |
bezeichner | gibt, wenn möglich, den Wert von |
Bezeichner: | 'Art' der Daten: | Beispiele für Notation der Daten: |
|---|---|---|
| Ganzzahl |
|
| Kommazahl |
|
| Zeichenkette |
|
| Wahrheitswert |
|
| einzelnes Zeichen |
|
| Kommazahl (weniger genau) |
|
Mit jeder Klasse erzeuge ich auch einen neuen Datentyp. Der Datentyp hat denselben Bezeichner wie die Klasse.