Creating Generics
Part of the Object Oriented Programming section of Coddy's R journey. Lesson 27 of 57.
An S4 generic is created with setGeneric(). Its function body is standardGeneric("name"), the S4 counterpart of UseMethod(). setGeneric() returns the generic's name, which is printed when it is called at the top level:
setGeneric("area", function(shape) standardGeneric("area"))Output:
[1] "area"Wrap the call in invisible() to keep that line out of the output. setMethod("area", "Circle", ...) then adds the method for one class:
setClass("Circle", slots = c(r = "numeric"))
setClass("Square", slots = c(side = "numeric"))
invisible(setGeneric("area", function(shape) standardGeneric("area")))
setMethod("area", "Circle", function(shape) pi * shape@r^2)
setMethod("area", "Square", function(shape) shape@side^2)
cat(area(new("Circle", r = 1)), area(new("Square", side = 3)))Output:
3.141593 9A method has the same arguments as its generic. When the generic has ..., a method can add named arguments of its own after them:
setClass("Circle", slots = c(r = "numeric"))
invisible(setGeneric("describe", function(x, ...) standardGeneric("describe")))
setMethod("describe", "Circle", function(x, ..., digits = 2) {
paste("circle of area", round(pi * x@r^2, digits))
})
c1 <- new("Circle", r = 2)
print(describe(c1))
print(describe(c1, digits = 4))Output:
[1] "circle of area 12.57"
[1] "circle of area 12.5664"setMethod() needs the generic to exist already, so the file with setGeneric() is sourced first. A call for a class without a method stops with an error that names the signature:
invisible(setGeneric("area", function(shape) standardGeneric("area")))
setClass("Circle", slots = c(r = "numeric"))
setMethod("area", "Circle", function(shape) pi * shape@r^2)
area(42)Output:
Error: unable to find an inherited method for function ‘area’ for signature ‘shape = "numeric"’
Execution haltedChallenge
EasyShapes are S4 classes in three files. In Shape.R create the generics area(shape) and perimeter(shape) with setGeneric(), wrapped in invisible(). In Circle.R and Square.R the classes are declared; add the area and perimeter methods of each with setMethod().
The supplied code reads lines circle 2 or square 3 and prints circle: area 12.57, perimeter 12.57.
Your code goes in Shape.R, Circle.R and Square.R. main.R holds the supplied input/output code and cannot be edited.
Try it yourself
source("Shape.R")
source("Circle.R")
source("Square.R")
# Supplied input/output code: keep it as it is
input <- suppressWarnings(readLines(file("stdin")))
for (line in input) {
p <- strsplit(line, " ")[[1]]
s <- if (p[1] == "circle") new("Circle", r = as.numeric(p[2])) else new("Square", side = as.numeric(p[2]))
cat(sprintf("%s: area %.2f, perimeter %.2f", p[1], area(s), perimeter(s)), "\n", sep = "")
}
This lesson includes a short quiz. Start the lesson to answer it and track your progress.
All lessons in Object Oriented Programming
1S3 Basics
Working With FilesLists With a ClassConstructor FunctionsPrint and Format MethodsValidators and HelpersRecap - Temperatures4Encapsulation in R
Closures as ObjectsEnvironments as ObjectsAccessor FunctionsGuarding StateRecap - Parking Meter2S3 Generics and Methods
Generics and UseMethodDefault MethodsMethods for Base GenericsNextMethod BasicsRecap - Shape Areas8Reference Classes
Defining Reference ClassesMethods and Field UpdatesCopy SemanticsInheritance and callSuperRecap - Task Queue11Project: Library Management
Books and MembersBorrowing Books3S3 Inheritance
Class VectorsMethod Lookup OrderExtending With NextMethodChecking With inherits()Recap - Staff Payroll6S4 Generics and Methods
Creating GenericsMethods for Built-insDispatch on Two ArgumentssetValidity and validObjectRecap - Money and CurrencyPractice on your own: Online R compiler