Vererbung und callSuper
Teil des Abschnitts Objektorientierte Programmierung der R-Journey von Coddy. Lektion 40 von 57.
contains = "Account" sorgt dafür, dass eine Referenzklasse jedes Feld und jede Methode von Account erbt. Die Unterklasse fügt ihre eigenen Felder und Methoden hinzu:
Account <- suppressMessages(setRefClass("Account",
fields = list(owner = "character", balance = "numeric"),
methods = list(deposit = function(x) balance <<- balance + x)
))
Savings <- suppressMessages(setRefClass("Savings", contains = "Account",
fields = list(rate = "numeric"),
methods = list(add_interest = function() deposit(balance * rate))
))
s <- Savings$new(owner = "Ada", balance = 100, rate = 0.05)
s$deposit(100)
s$add_interest()
cat(s$balance, is(s, "Account"))Ausgabe:
210 TRUEEine Unterklasse überschreibt eine Methode, indem sie eine Methode mit demselben Namen definiert. Darin ruft callSuper(...) die Version der Elternklasse auf, sodass das untergeordnete Objekt nur das hinzufügt, was neu ist:
Account <- suppressMessages(setRefClass("Account",
fields = list(balance = "numeric"),
methods = list(withdraw = function(x) {
if (x > balance) stop("insufficient funds", call. = FALSE)
balance <<- balance - x
})
))
Savings <- suppressMessages(setRefClass("Savings", contains = "Account",
methods = list(withdraw = function(x) {
if (x > 500) stop("limit is 500", call. = FALSE)
callSuper(x)
})
))
s <- Savings$new(balance = 300)
s$withdraw(200)
for (x in c(600, 400)) cat(tryCatch({ s$withdraw(x); "ok" }, error = function(e) conditionMessage(e)), "\n")
cat(s$balance, "\n")Ausgabe:
limit is 500
insufficient funds
100 initialize wird wie jede Methode geerbt und überschrieben. Das initialize einer untergeordneten Klasse ruft callSuper(...) auf, damit die übergeordnete Klasse ihre Felder festlegt, und setzt anschließend ihre eigenen Standardwerte:
Account <- suppressMessages(setRefClass("Account",
fields = list(owner = "character", balance = "numeric"),
methods = list(initialize = function(...) {
initFields(...)
if (length(balance) == 0) balance <<- 0
invisible(.self)
})
))
Savings <- suppressMessages(setRefClass("Savings", contains = "Account",
fields = list(rate = "numeric"),
methods = list(initialize = function(...) {
callSuper(...)
if (length(rate) == 0) rate <<- 0.01
invisible(.self)
})
))
s <- Savings$new(owner = "Bo")
cat(s$owner, s$balance, s$rate)Ausgabe:
Bo 0 0.01Objekte verschiedener Unterklassen reagieren auf denselben Methodenaufruf auf ihre eigene Weise, sodass eine Schleife über eine gemischte Liste keine Überprüfungen der Klasse benötigt:
Account <- suppressMessages(setRefClass("Account",
fields = list(balance = "numeric"),
methods = list(month_end = function() balance <<- balance - 2)
))
Savings <- suppressMessages(setRefClass("Savings", contains = "Account",
methods = list(month_end = function() balance <<- balance * 1.01)
))
bank <- list(Account$new(balance = 100), Savings$new(balance = 100))
for (a in bank) a$month_end()
cat(vapply(bank, function(a) a$balance, numeric(1)))Ausgabe:
98 101Aufgabe
EinfachKonten und Sparkonten sind Referenzklassen in zwei Dateien. Vervollständige die Methoden:
AccountinAccount.R(Felderowner,balance):initializesetzt den Kontostand auf 0, wenn er nicht angegeben wurde;deposit(x);withdraw(x)bricht mitinsufficient fundsab;month_end()erhebt eine Gebühr von 1;describe()gibtada: 99zurück.SavingsinSavings.R(enthältAccount, Feldrate):withdraw(x)bricht bei einem Wert über 500 mitlimit is 500ab und ruft andernfallscallSuper(x)auf;month_end()addiert Zinsen,balance * rate, anstelle der Gebühr;describe()fügt mitcallSuper()(savings)hinzu.
Verwende call. = FALSE. Beide Definitionen sind in suppressMessages() eingeschlossen. Der bereitgestellte Code liest savings ada 0.1 oder account bo, anschließend dep 100, wd 30 oder month, und gibt nach jedem Schritt describe() oder refused: sowie die Nachricht aus.
Dein Code gehört in Account.R und Savings.R. main.R enthält den bereitgestellten Ein-/Ausgabecode und darf nicht bearbeitet werden.
Probier es selbst
source("Account.R")
source("Savings.R")
# Gelieferter Ein-/Ausgabe-Code: belasse ihn wie er ist
input <- suppressWarnings(readLines(file("stdin")))
p <- strsplit(input[1], " ")[[1]]
acc <- if (p[1] == "savings") Savings$new(owner = p[2], rate = as.numeric(p[3])) else Account$new(owner = p[2])
for (line in input[-1]) {
q <- strsplit(line, " ")[[1]]
msg <- tryCatch({
if (q[1] == "dep") acc$deposit(as.numeric(q[2]))
if (q[1] == "wd") acc$withdraw(as.numeric(q[2]))
if (q[1] == "month") acc$month_end()
acc$describe()
}, error = function(e) paste("refused:", conditionMessage(e)))
cat(line, ": ", msg, "\n", sep = "")
}
Diese Lektion enthält ein kurzes Quiz. Starte die Lektion, um es zu beantworten und deinen Fortschritt zu speichern.
Alle Lektionen in Objektorientierte Programmierung
1S3-Grundlagen
Arbeiten mit DateienListen mit einer KlasseKonstruktorfunktionenPrint- und Format-MethodenValidatoren und HilfsfunktionenRückblick – Temperaturen4Kapselung in R
Closures als ObjekteUmgebungen als ObjekteZugriffsfunktionenZustand schützenRückblick – Parkuhr2S3-Generics und Methoden
Generics und UseMethodStandardmethodenMethoden für Basis-GenericsGrundlagen von NextMethodWiederholung – Flächeninhalte von Formen5S4-Klassen
S4-Klassen definierenSlots und ZugriffGültigkeitsprüfungenShow-MethodenRückblick – Produktkatalog8Referenzklassen
Referenzklassen definierenMethoden und FeldänderungenKopiersemantikVererbung und callSuperZusammenfassung – Aufgabenwarteschlange11Projekt: Bibliotheksverwaltung
Bücher und MitgliederBücher ausleihenÜbe selbstständig: Online-R-Compiler