Monday, February 26, 2018

Java's Stack Class and Queue Interface

Neither java.util.Queue nor java.util.Stack enforce queue or stack disciplines. As such they are unsuitable for teaching queues and stacks, and unsuitable for use in software engineering.

If I have a Stack that is shared among various modules, perhaps maintained by various people, I want to know that no one is accessing it beyond push and pop at the top. Otherwise, there is no way to have any confidence that any contract or class invariants are being maintained.

I have no issue with Stack methods empty(), peek(), pop(), or push(). My objection to search is minor: this isn't part of the idea of the stack, so it should not be in the public interface.

But Stack also inherits a number of methods from java.util.Vector, including removeElementAt() and insertElementAt(). This is not a stack. It can be used as a stack, but the programmer has no assurance that other pieces of code are treating it as a stack.

The Queue interface is not as bad. The interface's six methods make it look like the committees that added to it over time were a  bit confused or at least not in agreement with one another, but, nonetheless, insertion is always at the tail, and removal is always at the head.

But Queue extends java.util.Collection, which adds a number of non-queue-like methods. Many of these methods allow treating a queue like a stream, which is more convenient than removing item-by-item, deciding if each item fits some criteria, and then adding those meeting the criteria back into a queue.

My main issue with java.util.Queue is shared with java.util.Stack: the is-a relation does not work. A subclass of either could satisfy the Liskov substitution principle, but the child would be neither stack nor queue.

One fix would be to define more reasonable Stack and Queue classes, and declare them final. What I really want is a way to declare an interface that restricts the addition of methods.

No comments: