在线时间:8:00-16:00
迪恩网络APP
随时随地掌握行业动态
扫描二维码
关注迪恩网络微信公众号
本节书摘来自华章出版社《Effective Ruby:改善Ruby程序的48条建议》一书中的第2章,第2.2节,作者 [美]彼得 J.琼斯(Peter J. Jones),更多章节内容可以访问云栖社区“华章计算机”公众号查看 第7条:了解super的不同行为假设你已经写好了一个类,这个类继承自一个基类。而被继承的类定义了一个不那么适合新类的方法。因此你决定改进那个方法,但是你不想完全替代既有方法,因为它承担了90%的必要且繁重工作。你也不想改变基类,因为那将破坏其他代码的功能。先忽略掉使用组合而非继承的这个更好的选项,让我们费点力气来重载这个方法。你最后做了这些改动: 你让自己陷入了困境。最大的问题是:你如何调用超类中的m1方法?如果你试图调用衍生类中的方法,你将陷入无限循环中。那没什么帮助。这就到了super出场的时候了: 现在我们有些眉目了。新版的m1方法使用super来调用父类的m1方法。本例中,super有点替代Base#m1的意思。你在能够直接使用Base#m1的任何地方都能使用super。这包括通过super传递你希望传递给Base类的m1的任何参数。(当然仅限于目标方法能够接收的那些参数。)小心这个陷阱。 即使super被用作并扮演得像个方法,它其实是Ruby的关键字。这之间的不同很重要,因为super是基于Ruby中设定为可选的元素——括号,来改变行为的。我说得很严肃哦,请仔细想上一秒。当使用super时,如果省略了括号是会改变其行为的。这比听上去还要严重。要知道括号改变了什么,我们需要回顾一下super的三种写法: super的第一种用法最为平常——就是我们之前所见的那样。如果你至少给super一个参数,那么它的行为就好像常规的Ruby方法,并且括号是可选的。通过这种形式,super将参数全部传递给目标方法。 如果没有给super任何参数和括号,它的行为可能会和你的预期不同。这种用法之下,super在调用目标方法时将宿主方法(enclosing method)的所有参数全部传递过去。如果存在方法块它也会一并传递。 你应该可以看出,这种形式下的super会带来一些副作用。首先,这样使用super仅仅在目标方法和宿主方法接收相同数量的参数时才可用。比如,这对我们之前看到的例子中的方法Base#m1和Derived#m1就不适用。试图在Derived#m1方法中使用无参的super会引发一个ArgumentError异常。如果被重载的方法不接收参数而宿主方法接收,那么着同样会为你带来一个相同的异常。 可以看出的第二件事与传递给重载方法的参数的值有关。如果你在使用无参形式调用super之前,在宿主方法中改变了其参数中的一个值,那么super会将改变后的当前值传递给目标方法,而非原始的值。这看起来非常合理,但有时你也需要注意。 当你希望在使用super时不将任何参数传递给重载方法,你需要使用空括号,即super()。在Ruby中这种形式看起来实在很怪。即使我们喜欢使用括号,在无参调用时也不会加上它们。这种super的用法看起来有些不自然,但这是无参(也没有块)调用重载方法的唯一途径。 以下是对上述规则的代码表述: 下一个需要关心的是super如何寻找要重载的方法。你也许会简单地想,只要调用其超类的同一方法就可以了,但这也简单地过分了哦。事实上,super是从整个继承体系中寻找的,见第6条,不过有些许微小的差异。使用super时,它从继承体系的上一层去寻找和当前方法同名的方法。所以,它将从当前类的上一层开始寻找。 这个例子也指出了使用super的一个限制。如果你希望调用一个定义在超类中的方法,而与此同时如果包含的模块中也定义了同名方法,super就无法帮你达到目的。显然,super会在找到第一个匹配的同名方法后停下来,而那是包含的模块中的方法,而不是超类中的。但如果你真的意外遇到了这种情况,那么可能在设计上存在严重的问题。可以考虑使用组合而非继承了。 Ruby会在内部追踪method_missing方法是否由一个错误的super调用引发,若是如此,会在异常中给出一些额外的信息。但这仅发生在Ruby没能在继承体系中找到已定义的method_missing方法而使用默认实现时。一旦继承体系中存在任何定义了method_missing方法的类,你就会丢失这个有用的细节。这个信息无法找回,即使你在自己的method_missing实现中调用super也不行。 |
2023-10-27
2022-08-15
2022-08-17
2022-09-23
2022-08-13
请发表评论