I can't find much on virtual/abstract classes in help(ReferenceClasses)
- can anyone provide a basic example in creating one? Moreover, how can I specify a virtual method and enforce that child classes must implement it?

- 173,410
- 32
- 338
- 418

- 9,808
- 17
- 72
- 125
2 Answers
Reference classes are S4 classes. SO maybe you should see the help of setClass
and Classes
:
Here a dummy example:
# virtual Base Class
setRefClass(
Class="virtC",
fields=list(
.elt="ANY"
),
methods = list(
.method = function(){
print("Base virtual method is called")
}
),
contains=c("VIRTUAL")
)
## child 1
## field as numeric and base .method is used
setRefClass(
Class="childNum",
fields=list(
.elt="numeric"
),
contains=c("virtC")
)
## child 2
## field is char and .method is overwritten
setRefClass(
Class="childChar",
fields=list(
.elt="character"
),
methods = list(
.method = function(){print('child method is called')}
),
contains=c("virtC")
)
## new('virtA') ## thros an error can't isntantiate it
a = new("childChar",.elt="a")
b = new("childNum",.elt=1)
b$.method()
[1] "Base virtual method is called"
a$.method()
[1] "child method is called"

- 9,303
- 17
- 30

- 119,832
- 17
- 199
- 261
-
Thanks, but while explicit instantiation of the virtual class `virtC` is caught, there appears to be no mechanism for ensuring that child classes actually implement all abstract methods inherited from the parent -- I am still able to declare class `childChar` without having implemented `$.method`. Any way to correct this behaviour? – mchen Jan 15 '14 at 17:09
-
1@MiloChen thats because R simply does not know about abstract methods. If you'd want to force that `.method` is overwritten, you could implement it in the virtual class with `stop( "virtC$.method() must be implemented" )`. – Beasterfield Jan 15 '14 at 17:13
-
@MiloChen I just add that Class are **virtual** and not **abstract**. I edit my example to show this feature. – agstudy Jan 15 '14 at 17:28
-
1I was afraid of that. I'd sincerely hoped for some mechanism to check for poorly-defined classes during their definition rather than relying on raising runtime errors. I mean, they've implemented checks for local assignment to field names (`field <- var` instead of `field <<- var`) ... – mchen Jan 15 '14 at 18:30
This is an approach I am thinking of taking to catch unimplemented methods from inherited VIRTUAL "interfaces". It doesn't catch an incorrect implementation statically, but it does error if you try to instantiate an object that doesn't implement all of an interfaces methods. Rather than wait for a runtime error when invoking an unimplemented function, this bombs as soon as you try to create an object.
Helper Functions
Helper function to search the inheritance tree for interfaces. This is too basic right now. What I need to do is crawl the tree and check for parents that inherit the Interface class themselves...
.get_interface_methods <- function(.self) {
## Get the environment of the class being instantiated
env <- attributes(.self$.refClassDef)$refMethods
## get original interface methods
supers <- selectSuperClasses(class(.self))
methods <- unlist(lapply(supers, function(x) getRefClass(x)$methods()))
## check the body is NOT null in the concrete class environment
funs <- Filter(is.function, lapply(methods, get, envir=env))
null_fun_body <- vapply(Map(body, funs), is.null, T)
## return names of functions not implemented
vapply(funs[null_fun_body], attr, "", which="name")
}
Another helper function that is called when instantiating an object that contains one or more interfaces.
.validate_interface <- function(.self) {
methods <- get_interface_methods(.self)
## stop the world and print out the un-implemented methods
if (length(methods) > 0L) {
stop("Must implement interface methods: ", paste(methods, collapse = ", "))
}
}
The Classes
The Interface class simply calls the validate function during initialization. A class that inherits from Interface can register interface methods using any function that has a NULL
body. I created a simple helper for that.
setRefClass(
"Interface",
methods = list(
initialize = function() {
validate(.self)
}), contains="VIRTUAL")
InterfaceMethod <- function() NULL
Interfaces
Here I create two interfaces with dummy methods that have NULL
bodies.
## Create an interface to be implemented
setRefClass(
"ITest1",
contains=c("VIRTUAL", "Interface"),
methods = list(
foo = InterfaceMethod,
bar = InterfaceMethod,
baz = InterfaceMethod
))
## create a second interface
setRefClass(
"ITest2",
contains=c("VIRTUAL", "Interface"),
methods = list(
spam = InterfaceMethod,
ham = InterfaceMethod
))
Instantiating an Object
Finally, I create a class definition that is not virtual and that inherits both of my defined interfaces. In the definition, I implement two of the interface functions foo
& baz
but do not implement bar
, spam
, or ham
:
Obj <- setRefClass(
"Obj",
contains = c("ITest1", "ITest2"),
methods = list(
foo = function() "Implemented!",
baz = function() "Implemented!"
))
When I try to instantiate this object, I get an error.
> x <- Obj$new()
Error in validate(.self) :
Must implement interface methods: bar, ham, spam

- 39,946
- 18
- 70
- 84