Menu
Coddy logo textTech

Virtual Classes

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

A virtual class describes what a family of classes has in common, and cannot have objects of its own. Declare it with representation("VIRTUAL"); its subclasses use contains as usual:

setClass("Shape", representation("VIRTUAL"))
setClass("Circle", contains = "Shape", slots = c(r = "numeric"))
setClass("Square", contains = "Shape", slots = c(side = "numeric"))
cat(isVirtualClass("Shape"), is(new("Circle", r = 1), "Shape"), "\n")
cat(tryCatch({ new("Shape"); "made" }, error = function(e) "not allowed"), "\n")

Output:

TRUE TRUE 
not allowed 

A method for the virtual class works for every subclass. Here describe() for Shape calls area(), which each subclass implements, so the description is written once:

setClass("Shape", representation("VIRTUAL"))
setClass("Circle", contains = "Shape", slots = c(r = "numeric"))
setClass("Square", contains = "Shape", slots = c(side = "numeric"))
invisible(setGeneric("area", function(s) standardGeneric("area")))
invisible(setGeneric("describe", function(s) standardGeneric("describe")))
setMethod("area", "Circle", function(s) pi * s@r^2)
setMethod("area", "Square", function(s) s@side^2)
setMethod("describe", "Shape", function(s) sprintf("%s with area %.2f", class(s), area(s)))
for (s in list(new("Circle", r = 1), new("Square", side = 2))) cat(describe(s), "\n")

Output:

Circle with area 3.14 
Square with area 4.00 

A virtual class can declare slots, which every subclass then has:

setClass("Shape", representation("VIRTUAL", label = "character"))
setClass("Square", contains = "Shape", slots = c(side = "numeric"))
sq <- new("Square", label = "tile", side = 2)
print(slotNames("Square"))

Output:

[1] "side"  "label"

setClassUnion() makes a virtual class out of existing classes. A slot of that type accepts any of them, such as a contact that is either an email address or a phone number:

setClassUnion("Contact", c("character", "numeric"))
setClass("Friend", slots = c(name = "character", contact = "Contact"))
a <- new("Friend", name = "Ada", contact = "ada@x.org")
b <- new("Friend", name = "Bo", contact = 5550123)
cat(class(a@contact), class(b@contact), "\n")
cat(tryCatch({ new("Friend", name = "Cy", contact = TRUE); "ok" }, error = function(e) "refused"), "\n")

Output:

character numeric 
refused 
challenge icon

Challenge

Easy

Payments share the virtual class Payment (slot amount) and the generics fee() and receipt(), all in Payment.R. Complete:

  • in Payment.R, the receipt() method for Payment: card: 100.00 + 2.00 fee, built from the class name in lower case, amount and fee();
  • in Card.R, the class Card (contains Payment) and its fee(): 2% of the amount;
  • in Cash.R, the class Cash and its fee(): 0.

The supplied code reads lines card 100 or cash 20, prints each receipt and the total charged, then checks that Payment is virtual and cannot be created.

Your code goes in Payment.R, Card.R and Cash.R. main.R holds the supplied input/output code and cannot be edited.

Try it yourself

source("Payment.R")
source("Card.R")
source("Cash.R")

# Supplied input/output code: keep it as it is
input <- suppressWarnings(readLines(file("stdin")))
total <- 0
for (line in input) {
  p <- strsplit(line, " ")[[1]]
  amount <- as.numeric(p[2])
  pay <- switch(p[1], card = new("Card", amount = amount), cash = new("Cash", amount = amount))
  cat(receipt(pay), "\n", sep = "")
  total <- total + amount + fee(pay)
}
cat(sprintf("charged: %.2f", total), "\n", sep = "")
cat("Payment is virtual: ", isVirtualClass("Payment"), "\n", sep = "")
cat(tryCatch({ new("Payment", amount = 1); "created a Payment" }, error = function(e) "cannot create a Payment"), "\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