I have in mind a method that takes as an argument a collection that supports just the stack operations push
and pop
. What is the most appropriate interface for this?
public void foo(WhatShouldThisTypeBe<String> stack) {
stack.push(bar());
...;
String xyz = stack.pop();
}
My thoughts are:
I could make my method take a java.util.Stack
but that is a legacy class, with unwanted synchronization overhead, and shouldn't be used in 2023.
I could make it take a List
(which supports the equivalent operations addLast()
and removeLast()
) but that would put unnecessary demands on the concrete type, such as the ability to take the nth element.
I could make it take a Deque
but again that would put unnecessary demands on the concrete type, such as support for the Queue operations add
and remove
which are not required.
Is there a Java interface that suits stack operations more precisely than any of the above?
SequencedCollection
probably steers closest to what you're looking for; it has the obvious downside of not existing prior to JDK21, at which point Deque
is probably the closest you want, but, as you found, it adds a little too much.
Crucially for you, ArrayList
does implement SequencedCollection
.
Failing all these things, there really is no option. You're presumably looking for some type that both ArrayDeque
as well as ArrayList
implement, as 2 somewhat arbitrarily chosen types that can trivially support fast 'add at end' and 'remove last' operations which is all you really need. The only one that is halfway relevant is Collection
, but it does not offer 'remove from end', thus, cannot be used here.
Hence, if requiring JDK21 isn't an option, then you're either stuck making multiple methods (one that takes any Deque
, one that takes an ArrayList
for example), making wrappers (make your own interface, then write methods that wrap types well known to support it into an object that implements this interface):
public interface Stackish<E> {
void push(E elem);
E pop() throws NoSuchElementException;
public static <E> Stackish<E> of(ArrayList<E> list) {
return new Stackish<E>() {
@Override public void push(E elem) {
list.add(elem);
}
@Override public E pop() throws NoSuchElementException {
try {
return list.remove(list.size() - 1);
} catch (IndexOutOfBoundsException e) {
throw new NoSuchElementException();
}
};
}
}
This would have the rather obvious annoying factor that users of your library need to wrap their collections.