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

时间:2010-05-27 15:01:11

标签: java inheritance vector stack

通过扩展类Vector,Java的设计者能够快速创建类Stack。是什么 这种继承使用的消极方面,特别是对于类Stack?

非常感谢。

6 个答案:

答案 0 :(得分:25)

Effective Java 2nd Edition,Item 16:赞成组合而不是继承

  

继承仅适用于子类实际上是超类的子类型的情况。换句话说,只有在两个类之间存在“is-a”关系时,类 B 才应该扩展类 A 。如果您想要一个类 B 扩展一个类 A ,请问自己这个问题:每个 B 是否真的是 A < / em>的?如果你不能如实回答这个问题,那么 B 不应该扩展 A 。如果答案是否定的,通常情况是 B 应包含 A 的私有实例,并公开更小更简单的API; A 不是 B 的重要组成部分,只是其实现的细节。

     

Java平台库中存在许多明显违反此原则的行为。例如,堆栈不是向量,因此Stack不应扩展Vector。同样,属性列表不是哈希表,因此Properties不应扩展Hashtable。在这两种情况下,组合都是可取的。

本书更详细,并结合第17项:继承的设计和文档,或者禁止它,建议不要在设计中过度使用和滥用继承。

这是一个简单的示例,显示了Stack允许un - 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"

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

另见

答案 1 :(得分:22)

一个问题是Stack是一个类,而不是一个接口。这与集合框架的设计不同,其中您的名词通常表示为接口(例如,List,Tree,Set等),并且存在特定实现(例如,ArrayList,LinkedList)。如果Java可以避免向后兼容,那么更合适的设计是使用Stack接口,然后将VectorStack作为实现。

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

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

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

答案 2 :(得分:6)

Stack子类Vector公开不适合堆栈的方法,因为堆栈不是向量(它违反了Liskov Substitution Principle)。

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

我认为Joshua Bloch已经记录在案,说Stack子类Vector是一个错误,但不幸的是我找不到参考。

答案 3 :(得分:3)

好吧,Stack应该是一个界面。

Stack接口应该定义堆栈可以执行的操作。然后可能有Stack的不同实现在不同的情况下表现不同。

但是,由于Stack是一个具体的类,所以这不可能发生。我们仅限于堆栈的一个实现。

答案 4 :(得分:2)

除了上面提到的主要有效点之外,Stack继承自Vector的另一个大问题是Vector是完全同步的,所以无论你是否需要它都会得到这个开销(参见StringBuffer vs. StringBuilder)。就个人而言,每当我想要一个堆栈时,我倾向于使用ArrayDeque。

答案 5 :(得分:1)

它违反了我们所知道的关于继承的第一条规则:你能用一张笔直的面孔说出一个Stack IS-A Vector吗?显然不是。

另一个更合乎逻辑的操作是使用聚合,但IMO的最佳选择是使Stack成为可以通过任何适当的数据结构实现的接口,与C ++ STL类似(但不完全相同)确实