Есть ли способ инициализировать объект S4, чтобы был возвращен другой объект? - PullRequest
2 голосов
/ 22 июля 2011

У меня есть иерархия классов с суперклассом fb, в которой не должно быть никаких объектов (я пробовал виртуальные классы, но столкнулся с проблемой, что вы не можете инициализировать объекты из виртуальных классов).Кроме того, у меня есть два подкласса (foo, bar) с одинаковыми слотами.Теперь я хочу создать новый объект, используя метод initialize для суперкласса, который возвращает объект одного из подклассов на основе некоторого значения:

setClass("fb", representation( x = "numeric"))

setClass("foo", contains = "fb")
setClass("bar", contains = "fb")

setMethod("initialize", "fb", function(.Object, x) {
    if (x < 5) class(.Object) <- "foo"
    else class(.Object) <- "bar"
    .Object@x <- x
    .Object
})

> new("fb", x = 3)
Error in initialize(value, ...) : 
  initialize method returned an object of class "foo" instead of the required class "fb"

Очевидно (и, вероятно, по уважительным причинам), R запрещает это,Есть ли способ достичь того, что я хочу в методе, и не использовать конструкцию if-else при создании нового объекта?

1 Ответ

4 голосов
/ 22 июля 2011

S4 помогает нам раскрашивать линии. Таким образом, ваш класс fb должен быть виртуальным, а метод initialize не должен изменять класс .Object. Вы могли бы написать функцию fb, которая выполняет ваше условное создание.

setClass("fb", representation( x = "numeric", "VIRTUAL"))
setClass("foo", contains = "fb")
setClass("bar", contains = "fb")

fb <-
    function(x)
{
    if (x < 5) new("foo", x=x)
    else new("bar", x=x)
}

fb - это более удобный для пользователя конструктор, который отделяет интерфейс к вашей иерархии классов от его реализации, которая обычно считается хорошей.

И для чего стоит неявное ограничение на методы S4 initialize, так это то, что new("foo") (вызывая new с именем класса, но без дополнительных аргументов) должен работать (в противном случае возникают ошибки при попытке расширить foo) , Таким образом, парадигма для метода инициализации в соответствии с

setMethod(initialize, "foo", function(.Object, ..., x=1) {
    .Object <- callNextMethod(.Object, ...)
    .Object@x <- x
    .Object
})

хотя часто (как в этом случае, когда initialize просто выполняет назначение слотов), метод инициализации вообще не нужен. Обратите внимание на использование ..., позиционирование x (требующее, чтобы аргумент был назван в соответствующем вызове new) и использование значения по умолчанию.

...