5

我有一个基类(我们称之为“A”),它的表示对于许多其他类是通用的。

因此我定义了其他类,比如“B”,来包含这个类。

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

setClass("A", representation(a="character"))
setClass("B", contains="A", prototype(a = "hello"))

但它会产生错误:

Error in representation[!slots] : object of type 'S4' is not subsettable

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

setClass("B", contains="A")

然后破解我自己的生成器函数:

new_B <- function(...){ 
           obj <- new("B", ...)
           obj@a = "hello"
           obj
         }

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

4

2 回答 2

5

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

.A <- setClass("A", representation(a="character"))
.B <- setClass("B", representation(b="numeric"),
     prototype(a="hello"),
     contains="A")

.A()并替换对and的.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)

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
}

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

大多数情况下,这些只是我的意见。

于 2013-08-13T02:01:09.407 回答
2

您只需要命名参数:

setClass("A", representation(a="character"))
setClass("B", contains="A", prototype=prototype(a="hello"))
于 2013-08-12T22:27:07.773 回答