在 Ruby 中,“new”和“initialize”的关系是什么?如何在初始化时返回 nil?

60
我想要的是:
obj = Foo.new(0)  # => nil or false
这不起作用:
class Foo
  def initialize(val)
    return nil if val == 0
  end
end

我知道在C/C++/Java/C#中,我们不能在构造函数中返回一个值。

但是我想知道在Ruby中是否可能。


13
如果这是一个合理性检查,更友好的做法是引发一个异常,解释为什么你不能继续初始化。返回nil只会造成(严重的)混乱。没有人会期望一个新创建的对象是nil ;)。 - d11wtq
C 中没有构造函数。 - YoTengoUnLCD
3
@d11wtq的陈述有一个显著的例外,就是NilClass.new() - Joe Atzberger
1
你应该将 @Jörg W Mittag 的回答标记为正确答案。 - Redoman
5个回答

89
在 Ruby 中,'new' 和 'initialize' 之间的关系是什么? new 通常会调用 initialize。默认的 new 实现大致如下:
class Class
  def new(*args, &block)
    obj = allocate

    obj.initialize(*args, &block)
    # actually, this is obj.send(:initialize, …) because initialize is private

    obj
  end
end

当然,您可以覆盖它以执行任何您想要的操作。

如何在初始化时返回nil?

我想要的是:

obj = Foo.new(0)  # => nil or false

这不起作用:

class Foo
  def initialize(val)
    return nil if val == 0
  end
end
在Ruby中并不存在所谓的“构造函数”。在Ruby中,只有方法存在,并且它们可以返回值。
你遇到的问题简单来说就是想要改变一个方法的返回值,但是你却重载了另一个方法。如果你想改变bar方法的返回值,你应该重载bar而不是其他方法。
如果你想改变Foo::new的行为,那么你应该改变Foo::new方法:
class Foo
  def self.new(val)
    return nil if val.zero?
    super
  end
end

请注意,这是一个非常糟糕的想法,因为它违反了new的契约,即返回一个完全初始化、完全运行的类实例。


3
selfFoo。尝试 class Foo; p self end # => Foo - Jörg W Mittag
1
你可以提供一个更好的设计方案,而不是简单地说某些东西“不好”。也许像Metz的after_initialize模式或者验证器等等。 - Todd

51

这两种方法存在重要的区别。

new是一个方法,通常用于创建该类的实例(它处理了像分配内存这样的棘手问题,Ruby 会屏蔽这些问题,以便您不必过于深入地了解细节)。

然后,initialize是一个实例方法,告诉对象按照所请求的参数设置其内部状态。

这两个方法都可以根据您的需求进行重写。例如,如果需要聪明到能够执行此操作,Foo.new可能实际上会创建并返回FooSubclass的实例。

但通常最好将此类用例委托给其他类方法,这些类方法更明确地说明它们的作用,例如 Foo.relating_to(bar)。打破其他人对诸如new之类方法的期望,会让人们更加困惑,而不是在长远来看有所帮助。

例如,看看Singleton的实现,这是一个允许特定类仅存在一个实例的模块。它将new方法设为私有,并公开了一个instance方法,该方法要么返回对象的现有实例,要么调用new方法(如果尚未创建该实例)。


8
你可以像这样做:

您可以这样做:

class Foo

  def self.init(val)
    new(val) unless val == 0
  end

  def initialize(val)
    #...
  end
end

使用示例:

obj = Foo.init(0)
 => nil
obj = Foo.init(5)
 => #<Foo:0x00000002970a98>

在你的例子中,这个调用也会使用 new 吗? - OpenCoderX
如果你指的是当我们用0调用init的例子,那么不,new不会被调用。 - Flexoid

5

Wanting to do

class Foo
  def initialize(val)
    return nil if val == 0
  end
end

如果你的代码不一致,那么会使代码变得混乱。

如果你有

class Foo
  def initialize(val)
    return nil if val == 0
    @val = val
    @bar = 42
  end
end

如果你执行 Foo.new(1),你想得到什么返回值?你想要得到 42Foo#initialize 的返回值),还是想要得到一个 foo 对象?如果你希望执行 Foo.new(1) 后得到一个 foo 对象,那为什么你会期望 return nil 会使得 Foo.new(0) 返回 nil 呢?


-2

通过简单地创建一个对象变量来解决:

class Foo
   def initialize(val)
       @val = val
       return nil if @val == 0
   end
end
obj = Foo.new(0)

Output:-
=>#<Foo:0x1243b8 @val=0>

输出结果在不同的计算机上会有所不同。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接