补充我的评论而不是提供问题的新答案,这是一个解决方案,我们仍然按位置匹配参数(因为我们为 B 类指定了附加表示):
.A <- setClass("A", representation(a="character"))
.B <- setClass("B", representation(b="numeric"),
prototype(a="hello"),
contains="A")
.A()
and .B()
替换呼叫new("A")
and 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
or B
用于初始化继承的类)。
对于用户来说使用起来不太友好.A
or .B
直接,例如,因为签名只是...
因此将被记录为“参见 A 类插槽的定义”。这破坏了接口和实现的分离,而这是 OOP 的优势。此外,最后一个代码块中的一个或另一个行为(.B(.A(a=a), b=b)
or .B(a=a, b=b)
)可能不是本意。因此,提供一个函数is暴露给用户,也许做一些初始数据按摩
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
不必要的方法,这是一件好事,因为这些方法有一个复杂的合同 https://stackoverflow.com/questions/16247583/inheritance-in-r/16248773#16248773(他们应该初始化and是复制构造函数,正如我们在上面看到的,未命名参数应该初始化基类),但大多数人都错了。
大多数情况下,这些只是我的意见。