Menu
Coddy logo textTech

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 TRUE

Eine 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.01

Objekte 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 101
challenge icon

Aufgabe

Einfach

Konten und Sparkonten sind Referenzklassen in zwei Dateien. Vervollständige die Methoden:

  • Account in Account.R (Felder owner, balance): initialize setzt den Kontostand auf 0, wenn er nicht angegeben wurde; deposit(x); withdraw(x) bricht mit insufficient funds ab; month_end() erhebt eine Gebühr von 1; describe() gibt ada: 99 zurück.
  • Savings in Savings.R (enthält Account, Feld rate): withdraw(x) bricht bei einem Wert über 500 mit limit is 500 ab und ruft andernfalls callSuper(x) auf; month_end() addiert Zinsen, balance * rate, anstelle der Gebühr; describe() fügt mit callSuper() (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 = "")
}
quiz iconTeste dich selbst

Diese Lektion enthält ein kurzes Quiz. Starte die Lektion, um es zu beantworten und deinen Fortschritt zu speichern.

Alle Lektionen in Objektorientierte Programmierung

Übe selbstständig: Online-R-Compiler