Menu
Coddy logo textTech

Method Lookup Order

Part of the Object Oriented Programming section of Coddy's R journey. Lesson 13 of 57.

For every call, R walks the class vector from left to right and runs the first method it finds. If no class has a method, it uses the default. Different objects in one hierarchy can stop at different levels:

move <- function(x) UseMethod("move")
move.animal <- function(x) "walks"
move.bird <- function(x) "flies"
move.default <- function(x) "stays"
penguin <- structure(list(), class = c("penguin", "bird", "animal"))
dog <- structure(list(), class = c("dog", "animal"))
rock <- structure(list(), class = "rock")
cat(move(penguin), move(dog), move(rock))

Output:

flies walks stays

A child overrides a parent method by defining its own. The override affects only the child and anything below it; the parent and its other children keep the parent method:

move <- function(x) UseMethod("move")
move.animal <- function(x) "walks"
move.bird <- function(x) "flies"
move.penguin <- function(x) "swims"
penguin <- structure(list(), class = c("penguin", "bird", "animal"))
sparrow <- structure(list(), class = c("bird", "animal"))
cat(move(penguin), move(sparrow))

Output:

swims flies

Each generic is looked up on its own. One object can take sound() from its own class and move() from a parent, and a parent method can call other generics that dispatch on the child again:

sound <- function(x) UseMethod("sound")
move <- function(x) UseMethod("move")
describe <- function(x) UseMethod("describe")
sound.animal <- function(x) "..."
sound.dog <- function(x) "woof"
move.animal <- function(x) "walks"
describe.animal <- function(x) paste(x$name, "says", sound(x), "and", move(x))
rex <- structure(list(name = "Rex"), class = c("dog", "animal"))
print(describe(rex))

Output:

[1] "Rex says woof and walks"

For an object with a class attribute, dispatch looks only at that attribute and then the default. The object is still a list inside, but a method for list is not tried:

size <- function(x) UseMethod("size")
size.list <- function(x) "a list"
size.default <- function(x) "default"
bag <- structure(list(1, 2), class = "bag")
cat(size(list(1, 2)), "/", size(bag))

Output:

a list / default
challenge icon

Challenge

Easy

A square is a rectangle with equal sides. In Rectangle.R write the methods area.rectangle(), perimeter.rectangle() and name.rectangle(), which returns rectangle 2x3. In Square.R complete new_square(side), which calls new_rectangle() with the class square, and override only name() with name.square(), which returns square 2. Squares must use the rectangle's area() and perimeter().

The generics and new_rectangle() are given. The supplied code reads lines with two numbers (a rectangle) or one (a square) and prints square 2: area 4, perimeter 8.

Your code goes in Rectangle.R and Square.R. main.R holds the supplied input/output code and cannot be edited.

Try it yourself

source("Rectangle.R")
source("Square.R")

# Supplied input/output code: keep it as it is
input <- suppressWarnings(readLines(file("stdin")))
for (line in input) {
  n <- as.numeric(strsplit(line, " ")[[1]])
  s <- if (length(n) == 1) new_square(n[1]) else new_rectangle(n[1], n[2])
  cat(name(s), ": area ", area(s), ", perimeter ", 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