This work is licensed under the Creative Commons Attribution 4.0 International License. For questions please contact Michael Hahsler.
S3 does not use formal class definitions. Only a class attribute is set.
o <- sample(LETTERS[c(1:4,6)], 10, replace = TRUE)
class(o) <- "grades"
o
## [1] "C" "D" "A" "C" "C" "B" "B" "C" "A" "A"
## attr(,"class")
## [1] "grades"
str(o)
## Class 'grades' chr [1:10] "C" "D" "A" "C" ...
S3 uses a dispatching mechanism for generic functions (see, e.g., ? print
) and methods. Methods can be easily implemented for new classes. Note: the signature has to match the generic function definition!
Here is the generic for print
print
## function (x, ...)
## UseMethod("print")
## <bytecode: 0xd78098>
## <environment: namespace:base>
Often there is a default method (the class name is appended using a period)
print.default
## function (x, digits = NULL, quote = TRUE, na.print = NULL, print.gap = NULL,
## right = FALSE, max = NULL, useSource = TRUE, ...)
## {
## noOpt <- missing(digits) && missing(quote) && missing(na.print) &&
## missing(print.gap) && missing(right) && missing(max) &&
## missing(useSource) && missing(...)
## .Internal(print.default(x, digits, quote, na.print, print.gap,
## right, max, useSource, noOpt))
## }
## <bytecode: 0xb4ae68>
## <environment: namespace:base>
Note: To get the help page of the method, you often add the class name ? print.default
Write a custom print method:
print.grades <- function(x, ...) {
cat("Object of class", class(x), "with", length(x), "grades\n")
print(unclass(x))
}
o
## Object of class grades with 10 grades
## [1] "C" "D" "A" "C" "C" "B" "B" "C" "A" "A"
Write a custom mean method:
mean.grades <- function(x, ...) {
x <- factor(x, levels = LETTERS[c(1:4,6)])
x <- 5 - as.numeric(x)
mean(x)
}
mean(o)
## [1] 2.7
R has many generic functions already defined. Sometimes it is convenient to define your own. Note: Make sure it does not exist!
exists("print_gpa")
## [1] FALSE
print_gpa <- function (x, ...) {
UseMethod("print_gpa", x)
}
print_gpa
## function (x, ...) {
## UseMethod("print_gpa", x)
## }
print_gpa.grades <- function(x, ...) {
cat("Your grade point average is: ", mean(x), "\n")
}
print_gpa(o)
## Your grade point average is: 2.7
Inheritance can be implemented by using a vector of class names. Methods are chosen left to right.
class(o) <- c("grades", "letters")
str(o)
## Classes 'grades', 'letters' chr [1:10] "C" "D" "A" "C" ...
is(o, "grades")
## [1] TRUE
is(o, "letters")
## [1] TRUE
Best practice is to provide a constructor function.
grades <- function(x) {
if(!is.character(x)) stop("Only characters allowed!")
if(any(is.na(match(x, LETTERS[c(1:4,5)]))))
stop("Illegal grade!")
class(x) <- "grades"
x
}
grades(c("A", "A", "A"))
## Object of class grades with 3 grades
## [1] "A" "A" "A"
try(
grades(c(1, 2, 3))
)
try(
grades(c("A", "I"))
)
More information on S3 can be found using ? S3
and at http://adv-r.had.co.nz/S3.html
Final note: R also has a formal class system called S4 (? S4
) and reference classes (? ReferenceClasses
)