r-lib / R6

Encapsulated object-oriented programming for R
https://R6.r-lib.org
Other
410 stars 56 forks source link

Inheritance allows colliding public and private member names #168

Open jayqi opened 5 years ago

jayqi commented 5 years ago

Normally, when you try to define public and private members in the same class with colliding names, you get an error.

MyClass <- R6::R6Class(
    "MyClass",
    public = list(
        colliding_method = function() {"A"}
    ),
    private = list(
        colliding_method = function() {"B"}
    )
)
#> Error in R6::R6Class("MyClass", public = list(colliding_method = function() {: All items in public, private, and active must have unique names.

However, this check can get bypassed when you use inheritance. See example:

MyParentClass <- R6::R6Class(
    "MyParentClass",
    public = list(
        colliding_field = "A",
        colliding_method = function() {"A"}
    )
)

MyChildClass <- R6::R6Class(
    "MyChildClass",
    inherit = MyParentClass,
    private = list(
        colliding_field = "B",
        colliding_method = function() {"B"}
    )
)

myChildObj <- MyChildClass$new()
myChildObj$colliding_field
#> [1] "A"
myChildObj$colliding_method()
#> [1] "A"
myChildObj$.__enclos_env__$private$colliding_field
#> [1] "B"
myChildObj$.__enclos_env__$private$colliding_method()
#> [1] "B"

This can be a little confusing when using super$ because you don't specify whether you want a public or private method from the superclass. It seems like what happens is that it just gives you back the first one that it finds.

MyGrandchildClass <- R6::R6Class(
    "MyGrandchildClass",
    inherit = MyChildClass,
    public = list(
        super_method = function() {super$colliding_method()}
    )
)

myGcObj <- MyGrandchildClass$new()
myGcObj$super_method()
#> [1] "B"

Collisions between public members and active bindings through inheritance will error out but not until trying to initialize an object.

MyActiveParentClass <- R6::R6Class(
    "MyActiveParentClass",
    public = list(
        colliding_name = function() {"A"}
    )
)

MyActiveChildClass <- R6::R6Class(
    "MyActiveChildClass",
    inherit = MyActiveParentClass,
    active = list(
        colliding_name = function() {"B"}
    )
)
myActiveChildObj <- MyActiveChildClass$new()
#> Error in makeActiveBinding(name, active[[name]], public_bind_env): symbol already has a regular binding

However, collisions between private members and active bindings are allowed through.

MyActiveParentClass <- R6::R6Class(
    "MyActiveParentClass",
    private = list(
        colliding_name = function() {"A"}
    )
)

MyActiveChildClass <- R6::R6Class(
    "MyActiveChildClass",
    inherit = MyActiveParentClass,
    active = list(
        colliding_name = function() {"B"}
    )
)
myActiveChildObj <- MyActiveChildClass$new()
myActiveChildObj$colliding_name
#> [1] "B"
myActiveChildObj$.__enclos_env__$private$colliding_name()
#> [1] "A"

Seeing the same for both CRAN version 2.3.0 and the current GitHub version.

Examples created on 2018-11-11 by the reprex package (v0.2.1)