假设我有父类,其初始化程序的参数具有默认值:
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方法可以做到这一点?
答案 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