继承的爱恨情仇——一场钻石引发的血案
最近在看PHP手册,发现了一个稀奇古怪的新玩意——trait。
这引起了我极大的兴趣,由于PHP面向对象的部分有很大程度和Java类似,我就自觉不自觉地和Java对比着来看。
这又让我想起了那个古老的故事——单继承和多继承。
说到这个问题,我最先想起的应该是C++和Java
图片来源:
编程语言拟人化(1):Java、C++、Python、Ruby、PHP、C#、JS
趣文:编程语言拟人化(第二弹)
這系列文章的原始出處,是日本的「リクナビ NEXT」這家日本的人力公司的《Java、C++、Python…プログラミング言語擬人化計画! 》和《Perl、C、Scala…プログラミング言語擬人化計画 2》;他基本上是針對幾種常見的程式語言去設計了萌化的腳色、以及根據該語言發展歷史的人設。知乎用户:shawn sharp
链接:https://www.zhihu.com/question/25038841/answer/44433042
间接来源:知乎
众所周知,C++是个御姐,Java是个萝莉….啊不对….C++支持多继承而Java不支持。
咋一看,觉得可能是Java做对了,C++的是落后粗野的方法。可是真的是这样吗?
难道多继承真的一无是处,百害而无一利吗?
显然不是这样的,当我们要用多继承时,我们期待的到底是什么效果呢?
我们期待的是:可以直接拿来用的类成员函数(方法)。这很重要吗?对很重要,因为这正是代码复用所在。
可说到代码复用首先想到的应该是普通函数,它不行吗?不行。像是Java这样的语言他本来就不支持普通函数,再者也是更重要的,我们要类成员函数的意义在于他有面向对象的一系列“红利”,这才是我们关心的,比如:覆盖、访问控制、多态等等,所以这些特性是普通函数所无法替代的也是我们迫切所需的。
当然,得到了这些“红利”的同时,我们往往也在被“喂屎”,最著名的便是“钻石继承”问题。
图片来自维基百科:Multiple inheritance
钻石继承当然有很多弊端,最常见的便是当B和C同时继承自A,然后D又继承了B和C。那假设B和C中都有一个成员函数x(),那么在D中这个x()函数将会用谁的版本呢?B的?还是C的?还有就是,假设A有一个成员变量y,它的构造函数会初始化它,那在D的构造函数执行的时候,它要初始化几个y呢,在D中的y又是哪一个呢?
这一系列问题看着就让人头大…恩,事实是C++居然都能有相应对策(不得不说,御姐赛高)。可是真的是心累啊…谁想整天处理这些鬼东西!人类之所以强大在于把复杂的问题简化而解决之,而不是愣头去啃复杂问题!
所以这个时候,trait就来了!
真的没有一种既能享受面向对象“红利”,又不用解决这么令人掉头发的复杂问题的东西吗?!当然有啦!它就是trait。
那trait究竟是个什么东西呢?抛去具体语法不谈,trait就是一个没有构造函数,不能被实例化的抽象类。哦…那问题不是还没被解决吗?!这不还跟原来一个样吗!
其实一开始我也很疑惑,这家伙到底和抽象类有啥区别!其实啊,还真没啥,就一个重大区别,那就是:在遇到冲突时,它能指定到底用哪个版本的成员函数。恩?什么意思?
就像我上文提到的那个问题,在B和C中分别有一个x(),在D中就发生了冲突,不知道该用哪个版本了。trait能指定,现在,在D中,我们用哪个x(),并且!没有被指定的那个,就像是暂时被舍弃了,就像不存在一样。所以,这是关键,这就是比C++进步的地方,C++也能指定用那个版本,但是未被指定的那个也还是在的。只有未被指定的被舍弃,才能从根本上解决二义性问题。
这样我们就能好好地享用“红利”而不再担心被“喂屎”了!
语言的发展的过程就是博弈的过程,就是取舍的过程啊!至少在这一点上,看来是这样的。
最后附上一张PHP的,出处同上