在Ruby中调用超类初始化程序时避免重复命名参数默认值(2.1+)

时间:2015-05-07 22:47:01

标签: ruby superclass named-parameters

假设我有父类,其初始化程序的参数具有默认值:

class Parent
  attr_reader :foo
  def initialize(foo: 123)
    @foo = foo
  end
end

我想创建一个具有foo相同默认值的子类。如果我重复声明,我可以这样做:

class Child < Parent
  attr_reader :bar
  def initialize(foo: 123, bar: 456)
    super(foo: foo)
    @bar = bar
  end
end

然而,这意味着我必须两次写123。如果我试图避免重复它 -

class Child < Parent
  attr_reader :bar
  def initialize(foo:, bar: 456)
    super(foo: foo)
    @bar = bar
  end
end

- 现在这意味着以前可选的,默认的foo现在是必需的子类,我实际上并没有从默认值中获得任何好处。< / p>

我以为我可以将其默认为子类中的nil -

class Child < Parent
  attr_reader :bar
  def initialize(foo: nil, bar: 456)
    super(foo: foo)
    @bar = bar
  end
end

- 但没有; Child.new(bar:789).foo现在nil,我想要的是123

我不能完全抛弃争论 -

class Child < Parent
  attr_reader :bar
  def initialize(bar: 456)
    super(foo: foo)
    @bar = bar
  end
end

- 因为如果我尝试指定它(Child.new(foo: 345, bar:789)),我会unknown keyword: foo (ArgumentError)

有没有办法实际留下一个参数,而不是给它一个默认值?和/或允许初始化程序采用任意附加的命名paraemetrs并将它们传递给超类初始化程序的方法?

更新:我提出了以下黑客攻击(手动滚动我自己的'默认参数',基本上),但我对此并不满意。

class Parent
  attr_reader :foo
  def initialize(foo: nil)
    @foo = foo || 123 # faking 'default'-ness
  end
end

class Child < Parent
  attr_reader :bar
  def initialize(foo: nil, bar: 456)
    super(foo: foo)
    @bar = bar
  end
end

当然还有更多Ruby-ish方法可以做到这一点?

2 个答案:

答案 0 :(得分:4)

在Ruby 2.0+中,您可以使用双splat运算符。

def initialize(bar: 456, **args)
  super(**args)
  @bar = bar
end

一个例子:

[1] pry(main)> class Parent
[1] pry(main)*   def initialize(a: 456)
[1] pry(main)*     @a = a
[1] pry(main)*   end  
[1] pry(main)* end  
=> :initialize
[2] pry(main)> class Child < Parent
[2] pry(main)*   def initialize(b: 789, **args)
[2] pry(main)*     super(**args)
[2] pry(main)*     @b = b
[2] pry(main)*   end  
[2] pry(main)* end  
=> :initialize
[3] pry(main)> ch = Child.new(b: 3)
=> #<Child:0x007fc00513b128 @a=456, @b=3>
[4] pry(main)> ch = Child.new(b: 3, a: 6829)
=> #<Child:0x007fc00524a550 @a=6829, @b=3>

双splat运算符类似于单个splat运算符,但它不是将所有额外的args捕获到数组中,而是将它们捕获到散列中。然后当用作super的参数时,双splat将散列变平为命名参数,类似于单个splat对数组的影响。

答案 1 :(得分:0)

这更像是一种Ruby-1.9-ish方式,而不是Ruby-2.1-ish方式,但我相信它可以做到你想要的,同时避免重复:

class Parent
  attr_reader :foo
  def initialize(opts = {})
    @foo = opts[:foo] || 123
  end
end

class Child < Parent
  attr_reader :bar
  def initialize(opts = {})
    super(opts)
    @bar = opts[:bar] || 456
  end
end

puts Parent.new.foo # => 123
puts Parent.new(foo: 1).foo # => 1
puts Parent.new(bar: 2).foo # => 123
puts Parent.new(foo: 1, bar: 2).foo # => 1