如何为继承的槽定义S4原型

cbo*_*tig 5 oop r s4

我有一个基类(我们称之为"A"),其表示形式与许多其他类相同.

因此,我定义其他类,如"B",以包含此类.

我想设置这些其他类(B)的原型,以包含从A继承的插槽的默认值.我认为这很自然:

setClass("A", representation(a="character"))
setClass("B", contains="A", prototype(a = "hello"))
Run Code Online (Sandbox Code Playgroud)

但它会产生错误:

Error in representation[!slots] : object of type 'S4' is not subsettable
Run Code Online (Sandbox Code Playgroud)

不知道为什么会这样.如果我省略原型,我可以做:

setClass("B", contains="A")
Run Code Online (Sandbox Code Playgroud)

然后破解我自己的生成器功能:

new_B <- function(...){ 
           obj <- new("B", ...)
           obj@a = "hello"
           obj
         }
Run Code Online (Sandbox Code Playgroud)

然后根据原型创建我的对象new_B(),但与使用通用生成器new("B")和我的原型相比,这非常粗糙和丑陋......

Mar*_*gan 5

补充我的评论而不是提供问题的新答案,这里是一个解决方案,我们仍然按位置匹配参数(因为我们为B类指定了额外的表示):

.A <- setClass("A", representation(a="character"))
.B <- setClass("B", representation(b="numeric"),
     prototype(a="hello"),
     contains="A")
Run Code Online (Sandbox Code Playgroud)

.A()和.B()替换调用new("A")和new("B").在某种程度上,这是语法糖,但可以使对象构造更透明

## construct an object using B's prototype, like new("B", b=1:3)
> .B(b=1:3)
An object of class "B"
Slot "b":
[1] 1 2 3

Slot "a":
[1] "hello"

## construct an object using A's prototype, like new("B", new("A"), b=1:3)
> .B(.A(), b=1:3)
An object of class "B"
Slot "b":
[1] 1 2 3

Slot "a":
character(0)
Run Code Online (Sandbox Code Playgroud)

(第二个示例使用了无名参数new或B用于初始化继承类的事实).

用户不必非常友好地使用.A或.B直接使用,例如,因为签名是公正的...,因此将被记录为"参见A类槽的定义".这破坏了接口和实现的分离,这是OOP的优势.此外,最后一个代码块(.B(.A(a=a), b=b)或.B(a=a, b=b))中的一个或另一个行为可能不是意图.因此,而不是提供一个功能被暴露给用户,也许做了一些初步的数据按摩

A <- function(a=character(), ...) {
    ## nothing special, just a public constructor
    .A(a=a, ...)
}

B <- function(b, a="hello", ...) {
    a <- tolower(a)  ## no SHOUTing!
    .B(A(a=a), b=b)  ## A's public interface; no need for B to know A's details
}
Run Code Online (Sandbox Code Playgroud)

函数A和B定义接口,为用户提供有关可接受参数的提示,而无需将构造函数绑定到类定义,并执行初步数据按摩.后者可以使initialize方法变得不必要,这是一件好事,因为它们有一个复杂的契约(它们应该初始化并且是复制构造函数,正如我们在上面看到的那样,未命名的参数应该初始化基类),这是大多数人得到的错误.

这些只是我的意见.


Pey*_*ton 2

您只需命名参数:

setClass("A", representation(a="character"))
setClass("B", contains="A", prototype=prototype(a="hello"))
Run Code Online (Sandbox Code Playgroud)

  • 另外,这并不是问题的一部分, `.A &lt;- setClass("A",representation(a="character"))` 允许 `.A()` 或 `.A(a="foo")`作为构造函数,而不是 `new("A")`。我使用 `.A &lt;- ...` 而不是 `A &lt;- ...` 因为我仍然认为人们经常会编写一个带有命名参数等的更正式的构造函数,调用 `.A()` 作为其最终的线。 (2认同)