web-dev-qa-db-ja.com

Rubyでは、「新規」と「初期化」の関係は何ですか?初期化中にnilを返す方法は?

私が欲しいのは:

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

これは機能しません:

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

C/C++/Java/C#では、コンストラクターで値を返すことはできません。

しかし、Rubyでそれが可能かどうか疑問に思っています。

48
Chris Xue

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

C/C++/Java/C#では、コンストラクターで値を返すことはできません。

しかし、Rubyでそれが可能かどうか疑問に思っています。

Rubyにはconstructorのようなものはありません。 Rubyにはメソッドのみがあり、値を返すことができます。

あなたが見ている問題は、単にあるメソッドの戻り値を変更したいのに、別のメソッドをオーバーライドしているということです。メソッドbarの戻り値を変更する場合は、他のメソッドではなく、barをオーバーライドする必要があります。

Foo::newの動作を変更する場合は、Foo::newを変更する必要があります。

class Foo
  def self.new(val)
    return nil if val.zero?
    super
  end
end

ただし、これは本当に悪い考えであることに注意してください。これは、newの規約に違反しているためです。

79
Jörg W Mittag

2つの方法には重要な違いがあります。

newclassメソッドで、通常はクラスのインスタンスを作成します(これは、Rubyだからあなたはあまり汚くする必要はありません)。

次に、インスタンスメソッドであるinitializeは、要求されたパラメーターに従って内部状態を設定するようにオブジェクトに指示します。

これらのいずれかは、必要に応じてオーバーライドできます。たとえば、_Foo.new_は、実際にFooSubclassのインスタンスを作成して返すことがあります。

ただし、多くの場合、これらのようなユースケースを、Foo.relating_to(bar)のように、それらが行うことについてより明示的な他のクラスメソッドに委任する方が適切です。 newのようなメソッドが何をすべきかについて他の人々の期待を破ると、人々を長期的に助けるよりも混乱させることになります。

例として、特定のクラスのインスタンスを1つだけ存在させるモジュールであるSingletonの実装を見てください。 newメソッドをプライベートにし、オブジェクトの既存のインスタンスを返すinstanceメソッドを公開するか、まだ作成されていない場合はnewを呼び出します。

45
Gareth

次のようなことができます:

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>
5
Flexoid

やりたい

_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)を実行した場合、何を取得しますか? _42_(_Foo#initialize_の戻り値)、またはfooオブジェクトが必要ですか? Foo.new(1)fooオブジェクトが必要な場合、なぜ_return nil_がFoo.new(0)にnilを返すと期待しますか?

4
Andrew Grimm