我有一个基类(我们称之为“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")
并拥有我的原型相比,这非常粗糙和丑陋......
答案 0 :(得分:5)
补充我的评论而不是提供问题的新答案,这里是一个解决方案,我们仍然按位置匹配参数(因为我们为B类指定了额外的表示):
.A <- setClass("A", representation(a="character"))
.B <- setClass("B", representation(b="numeric"),
prototype(a="hello"),
contains="A")
.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)
(第二个例子使用了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
方法变得不必要,这是一件好事,因为它们有complicated contract(它们应该初始化和是复制构造函数,而且正如我们所做的那样看到上面未命名的参数应该初始化基类,大多数人都会出错。
这些只是我的意见。
答案 1 :(得分:2)
您只需要命名参数:
setClass("A", representation(a="character"))
setClass("B", contains="A", prototype=prototype(a="hello"))