从Vector继承的Java类Stack的负面影响是什么?

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)

这严重违反了堆栈抽象数据类型.

也可以看看


Uri*_*Uri 22

一个问题是Stack是一个类,而不是一个接口.这发散从集合框架的设计,你的名词通常表示为一个接口(例如,列表,树,设置等),并有具体的实施方式(例如,数组列表,链表).如果Java可以避免向后兼容,那么更合适的设计将是具有Stack接口,然后将VectorStack作为实现.

第二个问题是Stack现在绑定到Vector,通常避免使用ArrayLists等.

第三个问题是您无法轻松提供自己的堆栈实现,并且堆栈支持非堆栈操作,例如从特定索引获取元素,包括索引异常的可能性.作为用户,您可能还必须知道堆栈的顶部是在索引0还是在索引n处.该接口还公开了容量等实现细节.

在原始Java类库中的所有决策中,我认为这是一个更奇特的决策.我怀疑聚合会比继承贵得多.

  • Sun建议在Java 6及更高版本中使用`Deque`(如`ArrayDeque`)而不是Stack. (3认同)
  • @Bemrose:那是真的.但是,我实际上并不是它的忠实粉丝,因为它暴露了接口方法以从双方获取东西.DE性质对我来说似乎是一个实现细节.我想我是API纯粹主义者.顺便说一句,我总是讨厌STL如何创造"deque"的缩写词,因为在大多数口音中,它的发音都像"dequeue",导致一些混乱. (2认同)

Joh*_*ley 6

Stack子类Vector暴露不适合堆栈的方法,因为堆栈不是向量(它违反了Liskov替换原则).

例如,堆栈是LIFO数据结构,但使用此实现,您可以调用elementAtget方法来检索指定索引处的元素.或者您可以insertElementAt用来破坏堆栈合同.

我认为约书亚布洛赫已经记录说有Stack子类Vector是一个错误,但不幸的是我找不到参考.