Search code examples
stackimmutabilitymutable

Mutable vs. Immutable


What should be considered to make a mutable class immutable? For example: Can we still have push and pop methods in an immutable stack class? Or we should simply remove any method that changes the state of the instantiated object?


Solution

  • The bottom line is: You'd be better to remove the method modifying the state of the instantiated object from the class. But if you want to keep it, then it should create a new object with a different state from the original one and return the new object back.

    Here is a more specific answer:

    There shouldn't be any methods changing the object state in an immutable class.

    There are a lot of void methods which change the state of this object in a mutable class. So we should change the signature of them in a way the return a new object instead of changing "this" object's state.

    Also there are a lot of non-void methods which change the state of "this" object and return the value they changed in "this". The signature of these methods should also be changed in a way that they return a new object instead of changing the state of "this". Talking about lists, usually another method (like "peek") is also needed to get a certain value. Check the sample bellow to get what do I mean:

    Check out these "push" and "pop" methods for a mutable stack class:

    public class Stack <E> {
    …
    public void push (E e) {
         ensureCapacity();  // This method checks for capacity
         elements[size++] = e;
       }
    

    This method adds a new element at the top of the stack and changes the state of "this" object in this way.

    public E pop () {
         if (size == 0) throw new IllegalStateException("Stack.pop");
         E result = elements[--size];
         elements[size] = null;
         return result;
       }
    …
    }
    

    This method removes the element at the top of the stack and returns it back and changes the state of "this" object by removing the element.

    Now, suppose that we need to change these methods to make this stack immutable. Let's deal with "push" method first:

    "push" is a void method which changes the state of "this" object by adding a new element to it. To make the stack immutable we will create a new stack similar to "this" and add the new element to this new stack and return it back:

    public class ImmStack <E> {
    ...
        /**
         * this method pushes the item to a new object and keeps the original object unchanged 
         * @param e The item to be pushed
         * @return A new list
         * @PRE An original list object is required as well as an item to be pushed
         * @POST A new list would be returned
         */
        @SuppressWarnings({ "unchecked", "rawtypes" }) // All items in this.elements[] are of type E
        public ImmStack<E> push (E e) {
    
             ImmStack<E> stc = new ImmStack(getNewElements());       
             stc.elements=ensureCapacity(stc.elements);
             stc.elements[size] = e;
             stc.size = size +1;         
             return stc;
           }
    

    "pop" method changes the state of "this" object by removing an element. To make the class immutable we will reate a new stack similar to "this" and remove the element from this new stack and return it back:

       /**
         * This pop method returns a new stack without the element at the top of the original list 
         * @return The new stack
         * @POST The new stack would be returned
         */
        @SuppressWarnings({ "unchecked", "rawtypes" }) // All items in this.elements[] are of type E
        public ImmStack<E> pop () {
    
            if (size == 0) throw new IllegalStateException("Stack.pop");
    
            ImmStack<E> stc = new ImmStack(getNewElements());        
             stc.elements=ensureCapacity(stc.elements);
             stc.elements[size-1] = null;
             stc.size=size-1;  
             return stc;
           }
    

    The old "pop" method was returning the element at the top. We also need a new method which returns the element at the top to cover this feature:

       /**
        * Returns item at front of queue without removing.
        * @return item at front
        * @throws java.util.NoSuchElementException if empty
        */
       public E top()
       {
        if (this.isEmpty())
            {
            throw new NoSuchElementException("Queue underflow");
            }
           return elements[size-1];
       }
    

    This was just an example. You might have more methods to change in your class to make it immutable.