Tutorial

Immutable Generic Classes

Boris Velichkov
Generic
Tutorial image via Shutterstock

In this article Dreamix Java software developer Boris Velichkov teaches us how to implement Immutable Generic Classes. It isn’t so difficult to make a class Immutable or Generic in Java. But what if you try to make the class both Immutable and Generic at the same time? Is it easy and possible?

Have you ever had to implement Immutable Generic Class? I’ve had a case where I’ve had to create a class of the type “Value Holder”. This class had to keep the value for a concrete field and a boolean variable that shows if this field is disabled or not. Once created, an object of this class shouldn’t be changed. This object will keep the value and a boolean flag for the field and won’t allow these value and flag to be changed. This means that it should be correct to make the class immutable. Also the class had to support different types for the value. The possible options were to use the java.lang.Object for a type (or to use a common super class if I know such class) or to make the class Generic. This led me to explore how to implement Immutable Generics and write this article.

It isn’t so difficult to make a class Immutable or Generic in Java. But what if you try to make the class both Immutable and Generic at the same time? Is it easy and possible?

First, let’s take a brief look at how to make a class Immutable and Generic separately.

Immutable Classes

The idea of an immutable class is to define objects that can’t be modified after creation, so once an object of this class is created it cannot be changed in any way. Such a class is the built-in Java String. Following Oracle’s documentation, the rules for creating an Immutable class are:

  1. Don’t provide “setter” methods — methods that modify fields or objects referred to by fields.

  2. Make all fields final and private.

  3. Don’t allow subclasses to override methods. The simplest way to do this is to declare the class as final. A more sophisticated approach is to make the constructor private and construct instances in factory methods.

  4. If the instance fields include references to mutable objects, don’t allow those objects to be changed:

    • Don’t provide methods that modify the mutable objects.

    • Don’t share references to the mutable objects. Never store references to external, mutable objects passed to the constructor; if necessary, create copies, and store references to the copies. Similarly, create copies of your internal mutable objects when necessary to avoid returning the originals in your methods.

Generic Classes

Generics in Java allow you to customize a generic method, class or Interface to the type you want. They add stability to the code by making bugs detectable at compile time.

The most commonly used type parameter names are:

  • E – Element (used extensively by the Java Collections Framework)

  • K – Key

  • N – Number

  • T – Type

  • V – Value

  • S,U,V etc. – 2nd, 3rd, 4th types

If you need more information about generics, see Oracle’s documentation.

Immutable Generic Classes

Let’s use the following class State to describe the Immutable Generics:

public class State {
    
    private String field;
    
    
    public State(String field) {
        this.field = field;
    }

    public String getField() {
        return field;
    }

    public void setField(String field) {
        this.field = field;
    }
}

If we want to make it Immutable it’s enough to follow the four steps above. After the first three it will look like:

public final class State {
    
    private final String field;
    
    public State(String field) {
        this.field = field;
    }

    public String getField() {
        return field;
    }
}

The fourth step is OK because our class State accepts and returns an immutable object – java.lang.String. But if this field is mutable it will allow our class to be modified by changing the reference. If we adjust it to the mutable class StringBuilder we must make a copy so that after changing the outer reference nothing will be changed inside the class. There are two standard ways to create a copy: using the method clone() or to use the copy-constructor.If we want to make it Immutable it’s enough to follow the four steps above. After the first three it will look like:

But not every object has them. In the case of the StringBuilder we could use a copy-constructor. The class should look like this:

public final class State {
    
    private final StringBuilder field;
    
    
    public State(StringBuilder field) {
        this.field = new StringBuilder(field);
    }

    public StringBuilder getField() {
        return new StringBuilder(field);
    }
}

Everything looks good but if we try to add Generics we are going to make our class accept different types. The problem is how to make sure that these types are immutable and if they aren’t, how to make a copy of them in runtime. The answer is – we cannot be sure about this.
We don’t know how to create an instance of this object or if this object has it’s own clone() method. It’s almost the same if we make the class accept java.lang.Object. This won’t allow us to create a copy due to the same reasons – the class Object has a clone() method but it is protected. This means only subclasses have access to this method. Also we will need to make a full copy including fields from the subclasses.

So what can we do?
First, we are going to make our class State generic and immutable only for the first three steps:

public final class State<T> {
    
    private final T field;
    
    
    public State(T field) {
        this.field = field;
    }

    public T getField() {
        return field;
    }
}

To implement the fourth step of immutability we must create a copy of the immutable objects, but we don’t know what kind of object is the type T. As I mentioned above there are two standard ways to make a copy. But not every class has a copy-constructor or implements the clone() method. One solution is to restrict the type T to be Cloneable. This means that the class State will accept only objects that implement the interface java.lang.Cloneable, so we are able to invoke the clone() method and make a copy. It’s good to mention that the interface Cloneable doesn’t explicitly oblige the developer to override the clone method and make it public. Based on the Java documentation: “By convention, classes that implement this interface should override Object.clone (which is protected) with a public method.”. Also we must use reflection to call the clone method. In this case our class State should look like this:

import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;

public final class State<T extends Cloneable> {
    
    private static Method clone;
    private final T field;
    
    
    public State(T field) throws NoSuchMethodException, SecurityException, IllegalAccessException, IllegalArgumentException, InvocationTargetException {
        if (clone == null) {
            clone = field.getClass().getMethod("clone");
        }
        this.field = (T) clone.invoke(field);
    }

    public T getField() throws IllegalAccessException, IllegalArgumentException, InvocationTargetException {
        return (T) clone.invoke(field);
    }
}

This could be done with a custom “Cloneable” interface. Also it could be restricted to accept only immutable classes or classes which implement or have the annotation “@Immutable”. But none of those solutions give exactly what we need. They only restrict the type T.
Another solution is to use reflection and to make a copy on our own (No matter whether the class has or doesn’t have a copy-constructor or implement the clone() method.). This works but it is a really BAD BAD practice, because it could affect the performance seriously.
Here we must mention that the object.getClass().newInstance() doesn’t help us because we need a copy, not just an instance of the class.

I will now present a brief example using reflection to make a shallow copy of an object.

First, we will need an instance of the type/class Unsafe, because we don’t know what constructors the class T has. Using Unsafe we can create an instance without calling a constructor and then populate it as we want.

private static Unsafe getUnsafe() {
    try {
        Field singleoneInstanceField = Unsafe.class.getDeclaredField("theUnsafe");
        singleoneInstanceField.setAccessible(true);
        return (Unsafe) singleoneInstanceField.get(null);
    } catch (IllegalArgumentException e) {
        e.printStackTrace();
    } catch (SecurityException e) {
        e.printStackTrace();
    } catch (NoSuchFieldException e) {
        e.printStackTrace();
    } catch (IllegalAccessException e) {
        e.printStackTrace();
    }
    return null;
}

Next we need to iterate through the super-class hierarchy of the class and get all fields that this class has (declared in the class or inherited by the parents) and copy/paste them to the clone object.

public static Object cloneByReflection(Object obj) {
    System.out.println(obj.getClass());
    Object cloneObj = null;

    try {
        cloneObj = getUnsafe().allocateInstance(obj.getClass());
    } catch (InstantiationException e) {
        e.printStackTrace();
        return null;
    }
    
    for (Class<? extends Object> objClass = obj.getClass(); !objClass.equals(Object.class); objClass = objClass.getSuperclass()) {
        Field[] fields = objClass.getDeclaredFields();
        for (int i = 0; i < fields.length; i++) {
            fields[i].setAccessible(true);
            try {
                Object object = fields[i].get(obj);
                fields[i].set(cloneObj, object);
            } catch (IllegalArgumentException e) {
                e.printStackTrace();
            } catch (IllegalAccessException e) {
                e.printStackTrace();
            }
        }
    }
    return cloneObj;
}

Finally, I would like to mention that this isn’t the full solution. When we make a copy we need to make a deep copy because if we make a copy of a field of a complex type, this field could have references to mutable objects and someone could change it. This change will affect our object again.
The above example uses reflection only to make a shallow copy. There are APIs like Java Deep Cloning Library, which make a deep copy using reflection. As I said before this will affect the performance a lot. Is it really worth it?

Do you have another solution?
What is the best one for you?
Have you ever had to implement Immutable Generics?

Author
Boris Velichkov
Boris Velichkov is a passionate Java software developer at Dreamix with rich experience in Oracle development as well as with strong interests in Artificial Intelligence and Machine Learning.

Comments
comments powered by Disqus