eom*_*off 17 java inheritance stack vector
通过扩展类Vector,Java的设计者能够快速创建类Stack.这种继承使用的负面影响是什么,特别是对于类Stack?
非常感谢.
pol*_*nts 25
有效的Java第2版,第16项:支持组合而不是继承:
只有在子类确实是超类的子类型的情况下,继承才是合适的.换句话说,只有在两个类之间存在"is-a"关系时,B类才应该扩展A类.如果你想让B级扩展A级,那么问问自己这个问题:每个B真的是A吗?如果你不能如实回答是这个问题,乙不应该延长一个.如果答案是否定的,通常情况是B应该包含A的私有实例并暴露更小更简单的API; 一个不是B的重要组成部分,只是其实施的细节.
Java平台库中存在许多明显违反此原则的行为.例如,堆栈不是向量,因此
Stack不应该扩展Vector.类似地,属性列表不是哈希表,因此Properties不应该扩展Hashtable.在这两种情况下,组合都是可取的.
本书更详细,并结合第17项:设计和文档继承或禁止它,建议不要在设计中过度使用和滥用继承.
这是一个简单的例子,它显示了Stack允许不Stack类似行为的问题:
Stack<String> stack = new Stack<String>();
stack.push("1");
stack.push("2");
stack.push("3");
stack.insertElementAt("squeeze me in!", 1);
while (!stack.isEmpty()) {
System.out.println(stack.pop());
}
// prints "3", "2", "squeeze me in!", "1"
Run Code Online (Sandbox Code Playgroud)
这严重违反了堆栈抽象数据类型.
在计算机科学中,堆栈是后进先出(LIFO)抽象数据类型和数据结构.
Uri*_*Uri 22
一个问题是Stack是一个类,而不是一个接口.这发散从集合框架的设计,你的名词通常表示为一个接口(例如,列表,树,设置等),并有具体的实施方式(例如,数组列表,链表).如果Java可以避免向后兼容,那么更合适的设计将是具有Stack接口,然后将VectorStack作为实现.
第二个问题是Stack现在绑定到Vector,通常避免使用ArrayLists等.
第三个问题是您无法轻松提供自己的堆栈实现,并且堆栈支持非堆栈操作,例如从特定索引获取元素,包括索引异常的可能性.作为用户,您可能还必须知道堆栈的顶部是在索引0还是在索引n处.该接口还公开了容量等实现细节.
在原始Java类库中的所有决策中,我认为这是一个更奇特的决策.我怀疑聚合会比继承贵得多.
让Stack子类Vector暴露不适合堆栈的方法,因为堆栈不是向量(它违反了Liskov替换原则).
例如,堆栈是LIFO数据结构,但使用此实现,您可以调用elementAt或get方法来检索指定索引处的元素.或者您可以insertElementAt用来破坏堆栈合同.
我认为约书亚布洛赫已经记录说有Stack子类Vector是一个错误,但不幸的是我找不到参考.