Getter
Standard-Getter
So wie der Compiler automatisch einen Standard-Setter hinzufügt, fügt er auch einen sogenannten Getter hinzu, der den lesenden Zugriff auf ein Attribut steuert. Auch den Standard-Getter siehst du nicht in dem von dir geschriebenen Code, aber er wird beim Übersetzen in das ausführbare Programm automatisch hinzugefügt.
Auch ein Getter erinnert an eine Methode, hat aber in Kotlin eine spezielle Syntax, die ihn von einer normalen Methode unterscheidet. Den Standard-Getter könnte man in Kotlin so schreiben, wenn man ihn selbst explizit angeben möchte:
class Timer {
var min: Int = 0
get() {
return field
}
// Rest der Klasse ...
}
fun main() {
val timer = Timer()
timer.min = 5 // Hier wird automatisch der Setter aufgerufen
println(timer.min) // Hier wird automatisch der Getter aufgerufen
}
Aufgabe 1
(a) Beschreibe den Aufbau eines Getters in Kotlin.
Experimentiere dazu mit dem obigen Code.
Versuche z.B. field umzubenennen
oder ändere die Formatierung wie z.B. die Einrückung oder Zeilenumbrüche.
(b) Ändere den Getter so ab, dass das Attribut min
unabhängig von dem Wert, der ihm zugewiesen wird, beim Lesen
immer den Wert 20 zurückgibt.
(Das ist natürlich nicht sinnvoll, aber es soll zeigen, wie ein
Getter funktioniert.)
Lesenden Zugriff auf Attribute steuern
Normalerweise gibt ein Getter einfach den Wert eines Attributs zurück, aber theoretisch könnte er auch eine Berechnung durchführen, bevor er einen Wert zurückgibt:
class FakeTimer {
var min: Int = 0
get() {
return field + 1
}
}
fun main() {
val timer = FakeTimer()
println(timer.min)
timer.min = 5
println(timer.min)
timer.min = 8
println(timer.min)
}
Aufgabe 2
Beschreibe den oben dargestellten Getter und formulieren eine Vermutung, welche Ausgabe das Hauptprogramm erzeugt. Überprüfe deine Vermutung, indem du das Hauptprogramm ausführst.
Abgeleitete Attribute
In der Praxis nutzt man selbst definierte Getter normalerweise natürlich nicht, um die
Werte von Attributen zu verfälschen, so wie das im obigen Beispiel gezeigt wird.
Man nutzt sie meistens, um Werte zu berechnen, die von anderen Attributen
abhängen.
Man nennt solche Attribute abgeleitete Attribute
oder auf Englisch auch computed properties.
Ein abgeleitetes Attribut selbst speichert keinen Wert,
sondern berechnet seinen Wert jedes Mal neu, wenn es gelesen wird.
Im Fall unseres Timers könnte man z.B. ein abgeleitetes Attribut zeitspanne
definieren, welches die Zeitspanne zwischen der minimalen und maximalen Zeit
berechnet und zurückgibt:
class Timer {
var min: Int = 0
var max: Int = 0
val zeitspanne: Int
get() {
return max - min
}
}
fun main() {
val timer = Timer()
// Hier solltest du den Timer noch testen
}
Einen setter für abgeleitete Attribute gibt es nicht, da sie selbst keinen eigenen Wert
speichern, sondern ihren Wert aus anderen Attributen berechnen.
Deshalb werden sie auch nicht mit var, sondern mit val
deklariert.
Aufgabe 3
(a) Betrachte die Implementierung des abgeleiteten Attributes zeitspanne
und beschreibe die Unterschiede zu einem normalen Attribut.
(b) Schreibe ein Hauptprogramm, das einen Timer erzeugt, die minimale und maximale Zeit setzt und die Zeitspanne ausgibt. Nutze dazu das BlueJ-Projekt oder die Online-Version oben.
Methode oder Attribut?
Abgeleitete Attribute könnten man eigentlich genauso gut mit Methoden implementieren, hier also z.B. mit einer Methode, die die Zeitspanne berechnet und zurückgibt. In einigen Sprachen gibt es keine abgeleiteten Attribute; dann muss man Methoden verwenden, um Werte zu berechnen, die von anderen Attributen abhängen. In Kotlin können wir entweder Methoden oder abgeleitete Attribute verwenden. Es empfiehlt sich aber nach Möglichkeit abgeleitete Attribute zu verwenden, da man dadurch besser zwischen Eigenschaften und Fähigkeiten eines Objekts, unterscheiden kann. Dass eine Eigenschaft berechnet wird und nicht als normales Attribut gespeichert wird, kann man auch im Klassendiagramm darstellen:
Wir verwenden im Folgenden - dort wo es möglich ist - eher abgeleitete Attribute (oder auf Englisch: computed properties) als Methoden.
Aufgabe 4
(a) Zeichne ein Klassendiagramm für die Klasse Timer,
in dem die Zeitspanne in einer Methode berechnet wird.
(b) Implementiere die Methode und teste sie in einem Hauptprogramm.
(c) Vergleiche die Methode mit dem abgeleiteten Attribut zeitspanne
in Bezug auf die Definition und die Verwendung.