你认为接口怎么样?
你的意识是Java或者C#的interface
是的,接口是一个好的语言特性吗?
当然,接口很好用!
真的吗,呃。接口是什么?一个类吗?
不是,和类不一样。
怎么说?
接口没有方法实现。
这是一个接口吗?
1 | public abstract class MyInterface { |
这不是,这是一个抽象类。
比起接口,有什么不同?
好吧,抽象类可以有方法实现。
对,但是这个没有,为什么它不是一个接口呢?
好吧,一个抽象类可以包含非静态变量,而接口不能。
对,但是这个也没有。再问,为什么它不是一个接口?
因为它本来就不是。
这不是一个令人满意的答案。如何区分它和接口呢?有什么是可以使用接口但是不能使用抽象类的呢?
类可以继承其他类,但是不能同时实现你的这个抽象类。
为什么不能?
因为,在Java里,你不能同时继承多个类。
为什么不能?
因为Java编译器不允许你这样做。
这太死板了。好吧,为什么不能实现那个抽象类而不是继承它呢?
因为编译器只允许你实现一个抽象接口。
这种规则太奇怪了。
才不是,这很合情合理。编译器允许你实现多个接口但是只能让你继承一个类。
为什么Java编译器允许你实现多个接口,但是不允许你继承多个类呢?
因为类多重继承很危险。
真的吗?怎么回事?
因为多重继承会导致致命方块。
天呐,听起来很可怕。什么是致命方块?
当某个类继承其他两个类,这两个类就同时继承另外一个类。
你的意思是:
1 | class B{} |
对,这很糟糕!
为什么这很糟糕?
因为类B可能有实例变量!
你的意思像这样?
1 | class B {private int i;} |
对呀!这样的话某个M的实例会有多少个i变量呢?
嗯,我明白了。由于D1和D2都有i变量,又因为M继承与D1和D2,这种情况下你也许期盼着M有两个不同的i变量。
对!但是由于M也间接继承与B,B只有一个i变量,你也许只期望M只包含一个i变量。
嗯,这种继承关系有点模糊不清。
对!
因此Java和C#不能有多重继承是因为可能造成致命方块问题?
不是的,因为每个人都可能造成致命方块问题由于所有的类都是继承与Object类的。
嗯,明白了。编译器作者不会把Object特殊处理吗?
呃…他们不会。
我有点好奇为什么呢?有其他编译器作者解决这个问题吗?
当然有,C++允许你创建致命方块问题。
对,我想Eiffel也可以。
对了,天呐,我想Ruby有解决方法。
是的,使用CLOS(common-lisp object system)—好吧,我们说致命方块问题几十年前就解决了并且不是致命的,也没有导致更坏的结果。
呃。对,我猜你是对的。
让我们回到我原来的问题上。为什么这不是一个接口?
1 | public abstract class MyInterface { |
因为它使用的关键字是class;而且这门语言也不允许你做多重继承。
对。所以关键字interface的发明是为了避免多重继承。
是的,可能是事实。
为什么Java或者C#的作者不适用已知的解决方案解决多重继承呢?
我不知道。
我也不知道,但是我可以猜。
你猜到了什么?
懒惰。
什么,懒惰?
对,它们根本不想处理这个问题。因此创建了一个新的东西绕过这个问题。这个东西就是interface。
你是说Java的作者创建interface是为了避免一些额外的工作量?
除此之外,我不能解释这个问题。
这有点太过鲁莽。我确信他们的意图肯定不只是这个。不管怎么样,有interface不会是什么坏事吧?我的意思是,它应该也没什么坏处吧?
尝试问你自己这个问题:为什么某个类需要知道它实现某个接口?这种东西是不是应该隐藏起来呢?
你的意思某个派生类应该知道这个并判断使用正确的关键字,extends或者implements,对吗?
对!如果你把class改成接口,有多少派生类需要同时修改?
都得改,至少在Java里面来说。在C#倒是解决了这个问题。
确实是这样。关键字implements和extends确实是多余而且危险的。Java还不如C#和C++使用冒号。
好,好吧,但是究竟什么时候需要使用多重继承呢?
下列情况我会用:
1 | public class Subject { |
哦,这是观察者模式!
是的,这是观察者模式——完全正确。
但是你不能编译因为你不能继承多个类。
对,这就很悲剧。
悲剧?但是为什么这样说呢?我的意思是你可以让MyWidget继承Subject类!
但是我并不想MyWidget知道任何关于观察者的信息。我想保持其关注点分离。观察者与小部件的关注点分离。
之后只需要在MyObservableWidget类实现register和notify方法。
什么?在每个观察者里面重复这些代码?我不认同这种做法。
在MyObservableWidget里持有Subject的引用把功能代理给Subject。
什么?在每个观察者里面重复这些代理代码?如此愚蠢。如此拙劣。呸。
好吧,你不得不在一个或多个观察者做这事。
我知道。但是我讨厌这样做。
呃,似乎也没办法避免。要么你违反关注点分离原则,要么重复部分代码。
是的。这种情况下语言强迫我不得不这样做。
是的,很不幸。
到底是语言的什么特性导致这种糟糕的局面呢?
关键字interface。
所以说…?
关键字interface没什么好处,接口无益。