Menu
CoddyTech

callNextMethod

本课属于 Coddy R 学习旅程的“面向对象编程”单元。第 33 课,共 57 课。

在 S4 方法中,callNextMethod() 会用相同的参数调用父类的方法,并返回它的结果。子类方法会在父类的答案基础上继续构建,而不是重复父类的内容:

setClass("Employee", slots = c(name = "character", salary = "numeric"))
setClass("Manager", contains = "Employee", slots = c(reports = "numeric"))
invisible(setGeneric("describe", function(x) standardGeneric("describe")))
setMethod("describe", "Employee", function(x) paste(x@name, "earns", x@salary))
setMethod("describe", "Manager", function(x) paste(callNextMethod(), "and manages", x@reports))
print(describe(new("Manager", name = "Ada", salary = 60000, reports = 4)))

输出:

[1] "Ada earns 60000 and manages 4"

这对 show() 也适用:子类会先打印父类的内容,然后打印自己的内容:

setClass("Employee", slots = c(name = "character"))
setClass("Manager", contains = "Employee", slots = c(reports = "numeric"))
setMethod("show", "Employee", function(object) cat("Employee:", object@name, "\n"))
setMethod("show", "Manager", function(object) {
  callNextMethod()
  cat("Reports:", object@reports, "\n")
})
new("Manager", name = "Ada", reports = 4)

输出:

Employee: Ada 
Reports: 4 

new() 会调用通用的 initialize()。你可以为它编写一个方法,用其他槽位的值填充槽位。它会先调用 callNextMethod(.Object, ...),因此常规设置和有效性检查仍会执行:

setClass("Order", slots = c(prices = "numeric", total = "numeric"))
setMethod("initialize", "Order", function(.Object, ...) {
  .Object <- callNextMethod(.Object, ...)
  .Object@total <- sum(.Object@prices)
  .Object
})
o <- new("Order", prices = c(2.5, 4, 1))
print(o@total)

输出:

[1] 7.5

与 S3 中的 NextMethod() 类似,callNextMethod() 会依次经过多个层级,每个类都会添加自己的部分:

setClass("Ticket", slots = c(base = "numeric"))
setClass("Vip", contains = "Ticket")
setClass("Backstage", contains = "Vip")
invisible(setGeneric("price", function(t) standardGeneric("price")))
setMethod("price", "Ticket", function(t) t@base)
setMethod("price", "Vip", function(t) callNextMethod() * 2)
setMethod("price", "Backstage", function(t) callNextMethod() + 50)
cat(price(new("Ticket", base = 30)), price(new("Vip", base = 30)), price(new("Backstage", base = 30)))

输出:

30 60 110
challenge icon

挑战

简单

票分为三个等级,每个等级都在单独的文件中。类和泛型 price() 已声明。请添加这些方法:

  • Ticket.R:price() 返回基础票价;show() 输出 Ticket for gala 并换行。
  • Vip.R:price() 返回票价的 1.5 倍;show() 输出票的那一行,然后输出 VIP lounge access。
  • Backstage.R:price() 返回 VIP 票价加 50;show() 输出 VIP 的各行,然后输出 Backstage pass。

在子类的方法中使用 callNextMethod()。提供的代码会读取各行 vip gala 80(等级、活动、基础票价),并输出每张票及其价格。

你的代码应写在 Ticket.R、Vip.R 和 Backstage.R 中。main.R 包含提供的输入/输出代码,不能编辑。

亲手试试

source("Ticket.R")
source("Vip.R")
source("Backstage.R")

# 提供的输入/输出代码:保持原样
input <- suppressWarnings(readLines(file("stdin")))
for (line in input) {
  p <- strsplit(line, " ")[[1]]
  base <- as.numeric(p[3])
  t <- switch(p[1],
    standard = new("Ticket", event = p[2], base = base),
    vip = new("VipTicket", event = p[2], base = base),
    backstage = new("BackstageTicket", event = p[2], base = base))
  print(t)
  cat(sprintf("price: %.2f", price(t)), "\n", sep = "")
}
quiz icon自我测试

这节课包含一个小测验。开始学习即可作答,并记录你的进度。

面向对象编程中的所有课

自己练习: R 在线编译器