从技术上讲,所有可以通过继承实现的东西也可以通过委托来实现。所以答案是“不”。
将继承转化为委托
假设我们有以下通过继承实现的类:
public class A {
String a = "A";
void doSomething() { .... }
void getDisplayName() { return a }
void printName { System.out.println( this.getDisplayName() };
}
public class B extends A {
String b = "B";
void getDisplayName() { return a + " " + b; }
void doSomething() { super.doSomething() ; ... }
}
这些东西很好用,调用printName
B 的实例将"A B"
在控制台中打印。
现在,如果我们用委托重写它,我们得到:
public class A {
String a = "A";
void doSomething() { .... }
void getDisplayName() { return a }
void printName { System.out.println( this.getName() };
}
public class B {
String b = "B";
A delegate = new A();
void getDisplayName() { return delegate.a + " " + b; }
void doSomething() { delegate.doSomething() ; ... }
void printName() { delegate.printName() ; ... }
}
我们需要printName
在 B 中定义,并在实例化 B 时创建委托。调用doSomething
将以与继承类似的方式工作。但是调用printName
将打印"A"
在控制台中。实际上,通过委托,我们失去了“this”绑定到对象实例和能够调用已被覆盖的方法的基本方法的强大概念。
这可以在支持纯委托的语言中解决。对于纯委托,委托中的“this”仍将引用 B 的实例。这意味着this.getName()
将从 B 类开始方法分派。我们实现与继承相同的效果。这是在基于原型的语言中使用的机制,例如具有委托的Self具有内置功能(您可以在此处阅读继承如何在 Self 中工作)。
但是Java没有纯委托。什么时候卡住了?不,真的,我们仍然可以自己做更多的努力:
public class A implements AInterface {
String a = "A";
AInterface owner; // replace "this"
A ( AInterface o ) { owner = o }
void doSomething() { .... }
void getDisplayName() { return a }
void printName { System.out.println( owner.getName() };
}
public class B implements AInterface {
String b = "B";
A delegate = new A( this );
void getDisplayName() { return delegate.a + " " + b; }
void doSomething() { delegate.doSomething() ; ... }
void printName() { delegate.printName() ; ... }
}
我们基本上是在重新实现内置继承提供的功能。是否有意义?不完全是。但它说明继承总是可以转换为委托。
讨论
继承的特点是基类可以调用在子类中被覆盖的方法。这就是模板模式的精髓。这样的事情不能通过委派轻松完成。另一方面,这正是使继承难以使用的原因。理解多态调度发生在哪里以及如果方法被覆盖会产生什么影响,需要精神上的扭曲。
有一些关于继承的已知陷阱以及它可能在设计中引入的脆弱性。特别是如果类层次结构不断发展。如果使用继承,也可能存在一些相等性问题。但另一方面,它仍然是解决一些问题的一种非常优雅的方式。hashCode
equals
此外,即使继承可以用委托代替,您也可以争辩说它们仍然实现不同的目的并相互补充——它们没有传达相同的意图,这不是纯粹的技术等效性所捕捉到的。
(我的理论是,当有人开始做 OO 时,我们很想过度使用继承,因为它被认为是语言的一个特性。然后我们学习委托,这是模式/方法,我们也学会了喜欢它。一段时间后,我们在两者之间找到了平衡,并培养了直觉,在哪种情况下哪个更好。好吧,正如你所看到的,我仍然喜欢两者,并且在介绍之前都值得谨慎。)
一些文献
继承和委托是增量定义和共享的替代方法。人们普遍认为,委托提供了一个更强大的模型。本文展示了一种“自然”的继承模型,它捕获了委托的所有属性。独立地,证明了对委托捕获继承能力的某些限制。最后,概述了一个完全捕获委托和继承的新框架,并探讨了这种混合模型的一些后果。
面向对象编程中最有趣的——同时也是最有问题的——概念之一是继承。继承通常被认为是区分面向对象编程与其他现代编程范式的特征,但研究人员很少就其含义和用法达成一致。[...]
由于类的强耦合和继承引起的不需要的类成员的激增,使用组合和委托的建议已变得司空见惯。文献中对相应重构的介绍可能会让人们相信这种转变是一项简单的工作。[...]