Question

I have two simple classes:

.A1 <- setClass("A1", 
            representation=representation( x1 ="numeric"),
            prototype = prototype(x1 = 10))

.A2 <- setClass("A2", contains="A1",
           representation=representation(x2="numeric"),
           prototype = prototype(x2 = 10))

setMethod("initialize", "A2",
      function(.Object, ..., x2 = .Object@x2)
      {
        callNextMethod(.Object, ..., x2 = x2)
      })

Using this code everything works:

a1 <- .A1(x1 = 3)
initialize(a1)

a2 <- .A2(x1 = 2, x2 = 4)
initialize(a2, x2 = 3)

.A2(a1, x2 = 2)

An object of class "A2"  # WORKS!
Slot "x2":
[1] 2

Slot "x1":
[1] 3

In particular the last line work, so a1 gets copied inside the "A2" object. The problem is that if define "initialize" also for the base class the last line doesn't work anymore:

setMethod("initialize", "A1",
      function(.Object, ..., x1 = .Object@x1)
      {
        callNextMethod(.Object, ..., x1 = x1)
      })

## I have to redefine also this initializer, otherwise it will call
## the default initializer for "A1" (it was stored in some lookup table?)
setMethod("initialize", "A2",
      function(.Object, ..., x2 = .Object@x2)
      {
        # your class-specific initialization...passed to parent constructor
        callNextMethod(.Object, ..., x2 = x2)
      })

And now I get:

.A2(a1, x2 = 2)
An object of class "A2"  # BAD!
Slot "x2":
[1] 2

Slot "x1":
[1] 10

I guess there is something wrong with my initializer of "A1", any ideas? Thanks!

Was it helpful?

Solution

The A1 initialize method mis-behaves because x1 = .Object@x1 consults .Object, which for the constructor is the prototype of the class (for your example .A2(a1, x2=2), in the initialize,A1-method .Object is constructed from the prototype of A2, so x1 is assigned 10, and x1 = .Object@x1 = 10 over-writes the value provided by a1.

It's hard to know what a general solution is. You could test for missing-ness

setMethod("initialize", "A1", function(.Object, ..., x1) {
    if (!missing(x1))
        callNextMethod(.Object, ..., x1=x1)
    else 
        callNextMethod(.Object, ...)
})

or do something clever, maybe with match.call, to avoid a combinatorial problem with more than a couple of slots.

Another approach, which seems to be the one adopted in practice even though it really just side-steps the problem, is to avoid using an initialize method, and instead rely on a separate constructor to do any data massaging, as in the first code box of the Retaining copy construction section of this answer.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top