By extending class Vector, Java’s designers were able to create class Stack quickly. What are the negative aspects of this use of inheritance, particularly for class Stack?
Thanks a lot.
By extending class Vector, Java’s designers were able to create class Stack quickly. What are the negative aspects of this use of inheritance, particularly for class Stack?
Thanks a lot.
Well, Stack
should be an interface.
The Stack
interface should define the operations a stack can perform. Then there could be different implementations of Stack
that perform differently in different situations.
But, since Stack
is a concrete class, this cannot happen. We are limited to one implementation of a stack.
One problem is that Stack is a class, not an interface. This diverges from the design of the collection framework, where your noun is typically represented as an interface (e.g., List, Tree, Set, etc.), and there are specific implementations (e.g., ArrayList, LinkedList). If Java could avoid backward compatibility, then a more proper design would be to have a Stack interface, then VectorStack as an implementation.
A second problem is that Stack is now bound to Vector, which is generally avoided in favour of ArrayLists and the like.
A third problem is that you cannot easily provide your own stack implementation, and that stacks support very non-stack operations like getting an element from a specific index, including the potential for index exceptions. As a user, you may also have to know if the top of the stack is at index 0 or at index n. The interface also exposes implementation details such as capacity.
Of all the decisions in the original Java class library, I consider this one of the more peculiar ones. I doubt that Aggregation would have been much more expensive than inheritance.
Having Stack
subclass Vector
exposes methods that are not appropriate for a stack, because a stack is not a vector (it violates the Liskov Substitution Principle).
For example, a stack is a LIFO data structure yet using this implementation you can call the elementAt
or get
methods to retrieve an element at a specified index. Or you can use insertElementAt
to subvert the stack contract.
I think Joshua Bloch has gone on record as saying that having Stack
subclass Vector
was a mistake, but unfortunately I can't find the reference.
In addition to the main valid points mentioned above, another big problem with Stack inheriting from Vector is Vector is completely synchronized, so you get that overhead whether you need it or not (see StringBuffer vs. StringBuilder). Personally I tend to use ArrayDeque whenever I want a stack.
It violates the very first rule we all learned about inheritance: can you, with a straight face, say that a Stack IS-A Vector? Clearly not.
Another more logical operation would be to have used aggregation, but the best option IMO would be to have made Stack an interface which could be implemented by any appropriate data structure, similar (but not exactly the same) to what the C++ STL does.