1. 继承破坏了封装性,换句话说,子类依赖于父类的实现细节。
需要进一步说明,这与方法调用不同,方法调用也依赖被调用的方法,但是并不依赖细节,也就是说,只要方法整体实现没有问题,方法调用就没有问题,但是继承很容易改变父类实现的细节(所以父类中能写成final尽量写成final),即使父类整体没有问题,也有可能因为子类细节实现不当,而破坏父类的约束。这使得继承具有脆弱性。
或者说,继承让子类很容易破坏影响父类的约束,父类约束的实现依赖子类的实现,并且,父类在他的后续版本中可以获得新的方法,而这个会对子类造成影响,虽然子类的代码没有变化,当初子类实现的时候也不知道未来的变化,显得很无辜。
Here, overriding is the root of evil.....
继承使得到的API限制在原始想重用的实现上,导致可变更性变差,限制了你的类的性能。
继承使得有了更多机会来暴露了类内部的细节,比如Properties p的例子,p.getProperty(String key),p.get(String key)。
继承很容易传播现有API内部的缺陷。
2. 为什么复合/转发在做到重用的同时,不会有上述问题?
a. 通过接口,抓住需要重用类的功能特性,同时还获得了灵活性,继承只能重用某一个具体类的代码,但复用利用接口,可以重用很多接口类的代码。
b. 组合模式,包装类,也正是利用了这一点,获得了灵活性。
c. 缺点:转发方法调用的性能影响(可以忽略),包装对象导致的内存占用(这个在敏感的地方显得很重要,因为复用会导致产生更多的对象。子类扩展属性的时候,如果继承的话,就只有属性本身内存的增加,如果复用的话,还需另外加一个属性宿主自身以及宿主引用的内存增加)
3. 什么时候用继承合适:当确实存在"is-a"关系的时候。
参考:
《Effective Java》第四章,第14条