Ответ 1
def Foo.new(arg=nil)
arg || super
end
У меня есть класс Foo. Я хочу иметь возможность передать конструктору экземпляр Foo, foo и вернуть тот же экземпляр.
Другими словами, я хочу пройти этот тест:
class Foo; end
foo = Foo.new
bar = Foo.new(foo)
assert_equal foo, bar
Кто-нибудь знает, как я могу это сделать? Я пробовал это:
class Foo
def initialize(arg = nil)
return arg if arg
end
end
foo = Foo.new
bar = Foo.new(foo)
assert_equal foo, bar # => fails
но это не сработает.
Справка
ИЗМЕНИТЬ
Потому что многие люди просили мое обоснование:
Я делаю быстрый анализ большого количества данных (много TB), и у меня будет много примеров множества объектов. Для некоторых из этих объектов нет смысла иметь два разных экземпляра с одними и теми же данными. Например, одним из таких объектов является объект "окно" (как в временном окне), который имеет два свойства: время начала и время окончания. Я хочу иметь возможность использовать конструктор любым из этих способов и вернуть объект окна:
window = Window.new(time_a, time_b)
window = Window.new([time_a, time_b])
window = Window.new(seconds_since_epoch_a, seconds_since_epoch_b)
window = Window.new(window_obj)
window = Window.new(end => time_b, start => time_a)
...
Некоторый другой объект, который нуждается в окне, может быть создан таким образом:
obj = SomeObj.new(data => my_data, window => window_arg)
Я не обязательно знаю, что в window_arg, и мне все равно - он примет любой единственный аргумент, который может быть интерпретирован конструктором Window. Если у вас уже есть экземпляр Window, я бы скорее использовал этот экземпляр. Но работа по интерпретации выглядит как проблема конструктора Window. Во всяком случае, как я уже упоминал, я собираю много TB данных и создаю много примеров вещей. Если объект окна передается, я хочу, чтобы он просто был распознан как объект окна и использовался.
def Foo.new(arg=nil)
arg || super
end
В definition конструкторы предназначены для возврата вновь созданного объекта класса, членом которого они являются, поэтому, вы не должны не переопределять это поведение.
Кроме того, в Ruby new
вызывает initialize
где-то внутри своего тела метода, и его возвращаемое значение игнорируется, поэтому в любом случае значение, возвращаемое с initialize
, не возвращается из new
.
С учетом сказанного, я думаю, что в вашем случае вам может понадобиться создать метод factory, который будет возвращать разные объекты Foo
на основе аргументов, переданных методу factory:
class Foo
def self.factory(arg = nil)
return arg if arg.kind_of? Foo
Foo.new
end
end
foo = Foo.factory
bar = Foo.factory(foo)
assert_equal foo, bar #passes
initialize
вызывается new
, который игнорирует его возвращаемое значение. В основном метод по умолчанию new
выглядит так (за исключением того, что он реализован в C, а не в ruby):
class Class
def new(*args, &blk)
o = allocate
o.send(:initialize, *args, &blk)
o
end
end
Таким образом, вновь выделенный объект возвращается в любом случае, независимо от того, что вы делаете в initialize
. Единственный способ изменить это - переопределить метод new
, например:
class Foo
def self.new(arg=nil)
if arg
return arg
else
super
end
end
end
Однако я бы настоятельно советовал против этого, так как он противоречит многим ожиданиям, которые люди имеют при вызове new
:
new
вернет новый объект. Я имею в виду, что он даже называется new
. Если вы хотите использовать метод, который не всегда создает новый объект, вы, вероятно, должны называть его чем-то другим.Foo.new
вернет объект Foo
. Ваш код вернет все аргументы. То есть Foo.new(42)
вернет 42, Integer, а не объект Foo
. Поэтому, если вы собираетесь это сделать, вы должны хотя бы вернуть этот объект, если это объект Foo
.Не работает:
class Some
def self.new( str )
SomeMore.new( str )
end
end
#
the Some is parent of SomeMore
class SomeMore < Some
def initialize( str )
@str = str
end
end
код > Для этого конкретного случая использования может быть лучше использовать один из этих подходов.
class Foo
def self.new(args=nil)
@@obj ||= super(args)
end
end
class Foo
def self.new(args)
@@obj = super(args)
end
def self.new
@@obj
end
end
Это позволяет вам иметь только один объект, который создается, который может использоваться универсально, но возвращает объект класса Foo
, что делает его более подходящим для стандартных ожиданий метода new
, как указал Джейкоб вне.
Я понял это случайно, и это работает слишком хорошо. скажем, вы хотите, чтобы конструктор (по какой-либо причине) возвращал число с плавающей точкой:
class Decimal
# possibly a bug that this works
@@value =
def self.new(input)
if input.class == Integer
@@value = input.to_f
elsif input.class == Float
@@value = input
else
@@value = -1.0
end
end
end
x = Decimal.new(4)
y = Decimal.new(32.141)
puts x # 4.0
puts y # 32.141
Или в твоем случае, Foo:
class Foo
@@value =
def self.new(args)
# some code ...
@@value = Foo.new #:<= your return value goes here
end
end