I am coming from the Python world and at the beginning was hard to think in terms of R classes. Finally, I made it. I think.
I was able to convert Python and Java classes-like to S4 in R.
This package rODE was created with the specific purpose of helping the initiated to learn S4 classes. The package is about solving ordinary differential equations and contains a little bit of everything about S4 classes.
Hope it helps. Below the link:
https://github.com/f0nzie/rODE
The package is also in CRAN.
Now, on the answer. There are many ways of doing in R what you want. Here is the first way, using S4 classes and prototype to initialize the value of your variables A and B.
setClass("MyClass", slots = c(
A = "numeric",
B = "numeric"
),
prototype = prototype(
A = 1,
B = 2
)
)
# generic functions
setGeneric("hello", function(object, ...) standardGeneric("hello"))
setGeneric("goodbye", function(object, ...) standardGeneric("goodbye"))
setGeneric("ohDear", function(object, ...) standardGeneric("ohDear"))
# Methods of the class
setMethod("hello", "MyClass", function(object, ...) {
return("Hello")
})
setMethod("goodbye", "MyClass", function(object, ...) {
return("Goodbye")
})
setMethod("ohDear", "MyClass", function(object, ...) {
return("Have no clue")
})
# instantiate a class
mc <- new("MyClass")
# use the class methods
hello(mc)
goodbye(mc)
ohDear(mc)
The second way of doing this is using the initialize method.
setClass("MyClass", slots = c(
A = "numeric",
B = "numeric"
)
)
# generic functions
setGeneric("hello", function(object, ...) standardGeneric("hello"))
setGeneric("goodbye", function(object, ...) standardGeneric("goodbye"))
setGeneric("ohDear", function(object, ...) standardGeneric("ohDear"))
# Methods of the class
setMethod("initialize", "MyClass", function(.Object, ...) {
.Object@A <- 1
.Object@B <- 2
return(.Object)
})
# Methods of the class
setMethod("hello", "MyClass", function(object, ...) {
return("Hello")
})
setMethod("goodbye", "MyClass", function(object, ...) {
return("Goodbye")
})
setMethod("ohDear", "MyClass", function(object, ...) {
return("Have no clue")
})
# instantiate a class
mc <- new("MyClass")
# use the class methods
hello(mc)
goodbye(mc)
ohDear(mc)
mc@A # get value on slot A
mc@B # get value on slot B
A third way of doing this is by using a constructor and initializing the class variables outside the class with the constructor function:
setClass("MyClass", slots = c(
A = "numeric",
B = "numeric"
)
)
# generic functions
setGeneric("hello", function(object, ...) standardGeneric("hello"))
setGeneric("goodbye", function(object, ...) standardGeneric("goodbye"))
setGeneric("ohDear", function(object, ...) standardGeneric("ohDear"))
# Methods of the class
setMethod("initialize", "MyClass", function(.Object, ...) {
return(.Object)
})
# Methods of the class
setMethod("hello", "MyClass", function(object, ...) {
return("Hello")
})
setMethod("goodbye", "MyClass", function(object, ...) {
return("Goodbye")
})
setMethod("ohDear", "MyClass", function(object, ...) {
return("Have no clue")
})
# constructor function
MyClass <- function() {
myclass <- new("MyClass")
myclass@A <- 1 # assignment
myclass@B <- 2
return(myclass) # return the class initialized
}
# instantiate a class
mc <- MyClass()
# use the class methods
hello(mc)
goodbye(mc)
ohDear(mc)
mc@A # get value on slot A
mc@B # get value on slot B
This still has room for improvement since we shouldn't be dealing with the raw name of the slots outside the class. Encapsulation, remember? Here is the fourth and better way using setReplaceMethod:
setClass("MyClass", slots = c(
A = "numeric",
B = "numeric"
)
)
# generic functions
setGeneric("hello", function(object, ...) standardGeneric("hello"))
setGeneric("goodbye", function(object, ...) standardGeneric("goodbye"))
setGeneric("ohDear", function(object, ...) standardGeneric("ohDear"))
setGeneric("getA", function(object, ..., value) standardGeneric("getA"))
setGeneric("getB", function(object, ..., value) standardGeneric("getB"))
setGeneric("setA<-", function(object, ..., value) standardGeneric("setA<-"))
setGeneric("setB<-", function(object, ..., value) standardGeneric("setB<-"))
# Methods of the class
setMethod("initialize", "MyClass", function(.Object, ...) {
return(.Object)
})
# Methods of the class
setMethod("hello", "MyClass", function(object, ...) {
return("Hello")
})
setMethod("goodbye", "MyClass", function(object, ...) {
return("Goodbye")
})
setMethod("ohDear", "MyClass", function(object, ...) {
return("Have no clue")
})
setMethod("getA", "MyClass", function(object, ...) {
return(object@A)
})
setMethod("getB", "MyClass", function(object, ...) {
return(object@B)
})
setReplaceMethod("setA", "MyClass", function(object, ..., value) {
object@A <- value
object
})
setReplaceMethod("setB", "MyClass", function(object, ..., value) {
object@B <- value
object
})
# constructor function
MyClass <- function() {
myclass <- new("MyClass")
return(myclass) # return the class initialized
}
# instantiate a class
mc <- MyClass()
# use the class methods
hello(mc)
goodbye(mc)
ohDear(mc)
setA(mc) <- 1
setB(mc) <- 2
getA(mc) # get value on slot A
getB(mc) # get value on slot B
And the 5th way is by creating a class method to instantiate the class itself which is useful to validate the input even for missing parameters:
.MyClass <- setClass("MyClass", slots = c(
A = "numeric",
B = "numeric"
)
)
# generic functions
setGeneric("hello", function(object, ...) standardGeneric("hello"))
setGeneric("goodbye", function(object, ...) standardGeneric("goodbye"))
setGeneric("ohDear", function(object, ...) standardGeneric("ohDear"))
setGeneric("getA", function(object, ..., value) standardGeneric("getA"))
setGeneric("getB", function(object, ..., value) standardGeneric("getB"))
setGeneric("setA<-", function(object, ..., value) standardGeneric("setA<-"))
setGeneric("setB<-", function(object, ..., value) standardGeneric("setB<-"))
setGeneric("MyClass", function(A, B, ...) standardGeneric("MyClass"))
# Methods of the class
setMethod("initialize", "MyClass", function(.Object, ...) {
return(.Object)
})
# Methods of the class
setMethod("hello", "MyClass", function(object, ...) {
return("Hello")
})
setMethod("goodbye", "MyClass", function(object, ...) {
return("Goodbye")
})
setMethod("ohDear", "MyClass", function(object, ...) {
return("Have no clue")
})
setMethod("getA", "MyClass", function(object, ...) {
return(object@A)
})
setMethod("getB", "MyClass", function(object, ...) {
return(object@B)
})
setReplaceMethod("setA", "MyClass", function(object, ..., value) {
object@A <- value
object
})
setReplaceMethod("setB", "MyClass", function(object, ..., value) {
object@B <- value
object
})
setMethod("MyClass", signature(A="numeric", B="numeric"), function(A, B, ...) {
myclass <- .MyClass()
myclass@A <- A
myclass@B <- B
return(myclass)
})
# instantiate the class with values
mc <- MyClass(A = 1, B = 2)
# use the class methods
hello(mc)
goodbye(mc)
ohDear(mc)
getA(mc) # get value on slot A
getB(mc) # get value on slot B