R6
R6 copied to clipboard
Cannot override field with method in subclass
I just figured out that it's not possible to override a field with a method in a subclass. Paradoxically, the opposite direction works. Maybe this is the desired behavior or I overlooked this aspect in the package documentation/the other GitHub issues. If so, please let me know.
field → field works:
parent <- R6Class("parent", list(
x = 1
))
child <- R6Class("child", inherit = parent, list(
x = 2
))
parent$new()
# <parent>
# Public:
# clone: function (deep = FALSE)
# x: 1
child$new()
# <child>
# Inherits from: <parent>
# Public:
# clone: function (deep = FALSE)
# x: 2
field → method doesn't work:
parent <- R6Class("parent", list(
x = 1
))
child <- R6Class("child", inherit = parent, list(
x = function() 2
))
parent$new()
# <parent>
# Public:
# clone: function (deep = FALSE)
# x: 1
child$new()
# <child>
# Inherits from: <parent>
# Public:
# clone: function (deep = FALSE)
# x: 1
method → method works:
parent <- R6Class("parent", list(
x = function() 1
))
child <- R6Class("child", inherit = parent, list(
x = function() 2
))
parent$new()
# <parent>
# Public:
# clone: function (deep = FALSE)
# x: function ()
child$new()
# <child>
# Inherits from: <parent>
# Public:
# clone: function (deep = FALSE)
# x: function ()
parent$new()$x()
# [1] 1
child$new()$x()
# [1] 2
method → field works:
parent <- R6Class("parent", list(
x = function() 1
))
child <- R6Class("child", inherit = parent, list(
x = 2
))
parent$new()
# <parent>
# Public:
# clone: function (deep = FALSE)
# x: function ()
child$new()
# <child>
# Inherits from: <parent>
# Public:
# clone: function (deep = FALSE)
# x: 2
parent$new()$x()
# [1] 1
This is a bug. I think that it makes sense to disallow overriding in either direction, from method->field or field->method, because then the subclass will behave differently from the superclass.
Actually, I was a bit surprised it didn't work, because there is usually no type checking in R. I was trying to write a "template class" with a lot of NA fields, which the subclasses would override with useful methods. But probably you're right and it would be more sane to prohibit this behavior altogether.
I was wondering about the use case you had in mind. We've been thinking about interfaces for R6, but haven't yet decided on a course of action (#163).
Interfaces seem like the proper way to implement what I was trying to achieve. Anyway, I think it would be a good idea to make the "field → method", "method → field" behavior consistent. :)
I was trying to write a "template class" with a lot of NA fields, which the subclasses would override with useful methods. But probably you're right and it would be more sane to prohibit this behavior altogether.
@hriebl A different idea that works today is to define methods that don't do anything in your template class, or to be a little fancier, define methods that error out if called.
Thank you, @jayqi, I ended up doing something like that. The whole bug is not a big deal, you can definitely work around it, but it's a bit weird...
Is this related to the inability to access public fields via super$
?
library(R6)
MySuper <- R6Class(
"MySuper",
public = list(
public_member = NULL,
initialize = function(...) {
self$public_member <- 1
},
get_super_public_member = function() {
self$public_member
}
)
)
MySub <- R6Class(
"MySub",
inherit = MySuper,
public = list(
get_public_member = function() {
list(
self = self$public_member,
super = super$public_member
)
}
)
)
my_sub <- MySub$new()
my_sub$get_super_public_member()
#> [1] 1
my_sub$get_public_member()
#> $self
#> [1] 1
#>
#> $super
#> NULL
Created on 2020-12-11 by the reprex package (v0.3.0)
I also just stumbled over this issue. I initially had defined the method as a field = NULL in the super class and defined the according method in the subclass. Would be great if this behavior could be made consistent or described in the documentation.