Menu
CoddyTech

类向量

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

S3 继承来自类向量。一个具有 class = c("manager", "employee") 的对象既是经理,也是员工。如果泛型没有针对 manager 的方法,分派就会继续查找,并使用 employee 方法:

pay <- function(x) UseMethod("pay")
pay.employee <- function(x) x$salary / 12
m <- structure(list(name = "Ada", salary = 60000), class = c("manager", "employee"))
print(class(m))
print(pay(m))

输出:

[1] "manager"  "employee"
[1] 5000

在每个构造函数中手动编写类向量,会重复父类的详细信息。父类构造函数会接收一个 class 参数,并将它放在自身类的前面,而子类构造函数会调用父类构造函数:

new_employee <- function(name, salary, ..., class = character()) {
  structure(list(name = name, salary = salary, ...), class = c(class, "employee"))
}
new_manager <- function(name, salary, reports) {
  new_employee(name, salary, reports = reports, class = "manager")
}
m <- new_manager("Ada", 60000, 4)
print(class(m))
print(names(m))

输出:

[1] "manager"  "employee"
[1] "name"    "salary"  "reports"

父类构造函数中的 ... 会收集子类的额外字段,这里是 reports,并将它们放在父类自己的字段之后。普通员工完全没有 reports 字段:

new_employee <- function(name, salary, ..., class = character()) {
  structure(list(name = name, salary = salary, ...), class = c(class, "employee"))
}
e <- new_employee("Bo", 40000)
print(class(e))
print(names(e))

输出:

[1] "employee"
[1] "name"   "salary"

类向量的顺序很重要:最具体的类要放在最前面。如果父类在前,它的方法会优先使用,子类的方法则永远不会被使用:

title <- function(x) UseMethod("title")
title.employee <- function(x) "employee"
title.manager <- function(x) "manager"
right <- structure(list(), class = c("manager", "employee"))
wrong <- structure(list(), class = c("employee", "manager"))
print(title(right))
print(title(wrong))

输出:

[1] "manager"
[1] "employee"
challenge icon

挑战

简单

员工和经理的代码分别放在两个文件中。请在 Employee.R 中完成 new_employee(name, salary, ..., class = character()):它会返回一个包含 name、salary 以及从 ... 传入的所有额外字段的列表,并使用类向量 c(class, "employee")。然后在 Manager.R 中完成 new_manager(name, salary, reports),它会调用 new_employee(),传入额外字段 reports 和类 manager。

已提供 label() 和它的 employee 方法,因此经理也能获得标签,无需为经理单独编写方法。提供的代码会读取 employee Bo 40000 或 manager Ada 60000 4 这样的行,并输出类向量、字段名称和标签。

你的代码应写在 Employee.R 和 Manager.R 中。main.R 包含提供的输入和输出代码,不能编辑。

亲手试试

source("Employee.R")
source("Manager.R")

# 提供的输入和输出代码:保持原样
input <- suppressWarnings(readLines(file("stdin")))
for (line in input) {
  p <- strsplit(line, " ")[[1]]
  x <- if (p[1] == "manager") new_manager(p[2], as.numeric(p[3]), as.numeric(p[4])) else new_employee(p[2], as.numeric(p[3]))
  cat("class: ", paste(class(x), collapse = ", "), "\n", sep = "")
  cat("fields: ", paste(names(x), collapse = ", "), "\n", sep = "")
  cat(label(x), "\n", sep = "")
}
quiz icon自我测试

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

面向对象编程中的所有课

自己练习: R 在线编译器