Menu
Coddy logo textTech

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 9

A 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 halted
challenge icon

Challenge

Easy

Shapes 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 = "")
}
quiz iconTest yourself

This lesson includes a short quiz. Start the lesson to answer it and track your progress.

All lessons in Object Oriented Programming

Practice on your own: Online R compiler