Menu
Coddy logo textTech

継承とcallSuper

CoddyのRジャーニー「オブジェクト指向プログラミング」セクションの一部。レッスン 40/57。

contains = "Account" により、Reference Class は Account のすべてのフィールドとメソッドを継承します。サブクラスは独自のフィールドとメソッドを追加します。

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"))

出力:

210 TRUE

サブクラスは、同じ名前のメソッドを定義することでメソッドをオーバーライドします。その中で、callSuper(...)は親のバージョンを呼び出すため、子クラスは新しい部分だけを追加します。

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")

出力:

limit is 500 
insufficient funds 
100 

initialize は他のメソッドと同様に継承およびオーバーライドされます。子の initialize は callSuper(...) を呼び出して親にフィールドを設定させ、その後、自身のデフォルト値を設定します。

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)

出力:

Bo 0 0.01

異なるサブクラスのオブジェクトは、同じメソッド呼び出しに対してそれぞれ独自の方法で応答するため、混在したリストに対するループでクラスのチェックを行う必要はありません:

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)))

出力:

98 101
challenge icon

チャレンジ

簡単

アカウントと貯蓄口座は、2つのファイルに定義されたリファレンスクラスです。メソッドを完成させてください:

  • Account は Account.R にあり(フィールドは owner、balance)、initialize は指定されていない場合に残高を0に設定します;deposit(x);withdraw(x) は insufficient funds で停止します;month_end() は手数料として1を差し引きます;describe() は ada: 99 を返します。
  • Savings は Savings.R にあり(Account を含み、フィールドは rate)、withdraw(x) は500を超える場合に limit is 500 で停止し、それ以外の場合は callSuper(x) を呼び出します;month_end() は手数料の代わりに利息、つまり balance * rate を加算します;describe() は callSuper() を使って (savings) を追加します。

call. = FALSE を使用してください。両方の定義は suppressMessages() でラップされています。提供されているコードは savings ada 0.1 または account bo を読み取り、その後 dep 100、wd 30 または month を読み取り、各処理の後に describe() または refused: とメッセージを出力します。

コードは Account.R と Savings.R に記述してください。main.R には提供された入出力コードが含まれており、編集できません。

自分で試してみよう

source("Account.R")
source("Savings.R")

# 提供された入出力コード: そのままにしておく
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 icon腕試し

このレッスンには短いクイズがあります。レッスンを始めて解答し、進捗を記録しましょう。

オブジェクト指向プログラミングのすべてのレッスン

自分で練習してみよう: Rオンラインコンパイラ