Search code examples
javastackstack-pointer

Extending an ArrayList - fragile-base class


Examining the best practices in Java, we find avoiding inheritance is a good one. One of the reasons maybe illustrated in the following problem:

Here we have a sub-class "Stack" extends "ArrayList"

class Stack extends ArrayList
{   private int stack_pointer = 0;
    public void push( Object article )
    {   add( stack_pointer++, article );
    }
    public Object pop()
    {   return remove( --stack_pointer );
    }
    public void push_many( Object[] articles )
    {   for( int i = 0; i < articles.length; ++i )
            push( articles[i] );
    }
}

Let us say we are into adding to a stack using push() defined in the previous code, and then we want to clear the stack using clear() of the base-class -i.e ArrayList-

Stack a_stack = new Stack();
a_stack.push("1");
a_stack.push("2");
a_stack.clear();
  • Here the problem is

The code successfully compiles, but since the base class doesn't know anything about the stack pointer, the Stack object is now in an undefined state. The next call to push() puts the new item at index 2 (the stack_pointer's current value), so the stack effectively has three elements on it—the bottom two are garbage.

So my questions would be, why

base class doesn't know anything about the stack pointer

in other words, where is the state of the stack pointer being reserved?

source: Why extends is evil


Solution

  • The variable stack_pointer is a member of the Stack class so how could the ArrayList superclass have any idea about it? Since it can't and doesn't the call to clear() isn't going to do anything to it.

    You need to Override the clear() method in your Stack class.

    Something like

    @Override
    public void clear()
    {
       super.clear();
       stack_pointer = 0;
    }
    

    Then when a user calls clear() on a Stack it will cause the pointer to be reset.

    Also you need to be aware that a user can call the functions add(), insert() etc on the Stack which since they aren't overridden will call the ArrayList functions. This is likely not what you want.

    A better approach would be to create a Stack with an ArrayList inside so you can hide the methods you need to hide.

    Something like

    public class Stack
    {
        private ArrayList<Object> stack = new ArrayList<Object>();
        private int stack_pointer;
    
        public void push(Object obj)
        {
           stack.add(stack_pointer++, obj);
        }
    
        // Other methods
    
        public void clear()
        {
           stack.clear();
           stack_pointer = 0;
        }
    }