本章节,我们来了解下 Ruby 中的 super 关键字,我们会讲述以下内容
- 隐式参数
superVSsuper()super和块super和祖先链
隐式参数
当一个带参数的方法被其子类之一重写 ( override ) 时,那么可以在子类重写的方法中调用不带参数也不带括号的 super 会自动将子方法的参数传递给父方法。
我们用一个示例来演示下这个机制
class Parent def say(message) puts message end end class Child < Parent def say(message) super end end irb> Child.new.say('Hi!') Hi!
上面的范例中,类 Child 继承自 Parent 类。然后还重写了 Parent 类中的 Parent#say 方法。
在 Child#say 方法内部,我们使用 super 关键字调用父类相应的方法 Parent#say ,并且会把 message 参数自动传递给 Parent#say 方法。
使用 super 关键字,Ruby 会自动在 Child 的祖先链 ancestors chain 中查找 #say 方法,找到了之后就会自动调用该方法并传递 message 作为参数。
如果你对 「 祖先链 」 不熟悉,那么可以访问我们的 Ruby 中的对象模型 ( object model ) ( 上 ) 来了解详情。
但是
我想,你也有同样的疑问 :如果祖先链中的 Parent#say 是一个不带参数的方法,那结果会怎么样 ?
这就涉及到 super 和 super() 相关的知识了。
super VS super()
为了解决上面刚刚提到的问题,我们重新定义 Parent#say 方法,移除 message 参数,就像下面这样
class Parent def say puts "I'm the parent" end end class Child < Parent def say(message) super end end irb> Child.new.say('Hi!') ArgumentError (wrong number of arguments (given 1, expected 0))
运行上面的代码会发现抛出了 ArgumentError (wrong number of arguments (given 1, expected 0)) 异常。
因为 Parent#say 方法是一个不带参数的方法,它不需要任何参数,但在 Child#say 方法中的 super 关键字却会将参数 message 隐式的传递给 Parent#say。
为了避免这个问题,也为了解决这个问题,我们可以明确的指出 super 不需要把子类中相关的参数传递给父类。为此,我们需要在 super 后添加一对小括号 () ,即 super() 明确的调用父类而不传递任何参数
class Parent def say puts "I'm the parent" end end class Child < Parent def say(message) super() end end irb> Child.new.say('Hi!') I'm the parent
这就是 super 与 super() 的区别,super 会自动将子方法的参数传递给父方法,但 super() 则不会。
super 和块
因为 「 块 」 ( block ) 作为参数如此常见,那么我们要如何在子类中将一个块传递给父类呢 ?
不管三七二十一,我们先修改下代码再说,修改 Parent#say 方法接收一个块作为参数
class Parent def say yield end end class Child < Parent def say super end end irb> Child.new.say { puts 'Hi! Glad to know you Parent!' } Hi! Glad to know you Parent!
哇塞,使用 super 关键字运行正常。
使用 super 关键字会自动将传递给子类方法 Child.new.say 的块传递给父类 Parent#say 方法。
在 Parent#say 方法中,我们使用 yield 关键字捕获一个块,并且执行它。
更多
yield的知识,可以访问我们的 Ruby 中的 yeild 关键字 ( 上 ) 和 Ruby 中的 yeild 关键字 ( 下 )
super 和祖先链
为了测试更多祖先链的情况,我们修改下 Parent 类,让它继承自 GrandParent,并在 GrandParent 中也定义一个同样的 say 方法。
然后去掉 Parent 类中的 say 方法
class GrandParent def say(message) puts "GrandParent: #{message}" end end class Parent < GrandParent end class Child < Parent def say(message) super end end irb> Child.new.say('Hi!') GrandParent: Hi!
上面的代码中,Child#say 方法的 super 首先会尝试在 Parent 类中找寻 say 方法,如果找到了就调用该方法,如果没找到,就会继续在祖先链上查找,直到找到为止 GrandParent#say
所以,运行结果就是调用 GrandParent#say 方法输出 GrandParent: Hi!
有关 Ruby 中的 super 关键字知识就这些。