Rules of Immutability

July 17, 2012 »best-practice

In a perfect world, every value container (an object that only holds multiple fields and defines access methods for those) is immutable. Immutability should always be a design-goal, especially, when creating a library or API.

In this article, I’m going to explain what immutable objects are, why they are cool and what stumbling blocks you should watch out for.

Although immutability, as a design-pattern, has no origin in any particular language, the following examples (and most of the writing) focus on the situation in the Java programming language and might not hold for other languages.

For examples in other programming languages, the below linked Wikipedia article can be consulted, although it's not that extensive as the following exposition.

What makes an object “immutable”?

Wikipedia boils it down to one simple sentence:

In object-oriented and functional programming, an immutable object is an object whose state cannot be modified after it is created.

Let’s look at an example:

public final class GeoTag {

    private final int latitude;
    private final int longitude;
    private final Date timestamp;
    private final String message;

    public GeoTag(int latitude, int longitude, Date timestamp, String message) {
        this.latitude = latitude;
        this.longitude = longitude;
        this.timestamp = timestamp;
        this.message = message;
    }

    public int getLatitude() {
        return latitude;
    }

    public int getLongitude() {
        return longitude;
    }

    public Date getTimestamp() {
        return timestamp;
    }

    public String getMessage() {
        return message;
    }
}

This is a class which encapsulates multiple values into one single object. It provides geographical information for a message, including latitude and longitude, the date of creation and the message itself.

An instance of this class can only be created by using it’s constructor (which initializes and populates all fields with the given values). When the instance is created, there is no way you can change any of those values, since you don’t offer any setter-methods to do so.

By doing that, you assure that this object can not be manipulated after it’s creation. But why does this help you?

Why immutability is cool

As pointed out above, the object can not be manipulated or changed in any way after it was created. This ensures the consistency of the instance across the whole application.

You can access an immutable objects on multiple threads simultaneously, without the fear of any concurrent modification by another thread. Immutable objects are always synchronized. No extra work is needed.

This makes passing objects around multiple threads or across multiple application-modules simple. It’s the easiest approach to thread-safety.

Also, immutable objects make for the perfect Map-keys, because you don’t have to worry about their values (and therefor the key) changing, which would destroy the Maps invariants.

Designing immutable objects

In this section, I’m going to give some design-advices and show some possible weak-spots.

Finalize to freeze the state

Let’s take a closer look. Notice how all the fields are declared to be private final? The reason for that is, that we don’t want to change the values of our fields once they have been initialized. A final-declared field can’t be re-assigned with a new value.

This secures any primitive value-type as much as possible. Since the primitive types don’t offer any methods to change their state, they are now immutable.

Prevent extensibility

We also declared the class itself to be final. Finalized classes can’t be extended, which freezes their internals from being manipulated. For example, an “attacker” could create a subclass of our GeoTag-class and make it mutable:

public class GeoTagMutable extends GeoTag {

    private int mLatitude;
    private int mLongitude;

    public GeoTagMutable(int latitude, int longitude, Date timestamp, String message) {
        super(latitude, longitude, timestamp, message);
        this.mLatitude = latitude;
        this.mLongitude = longitude;
    }

    /**
     * Copy-constructor to make an <i>immutable</i> instance <i>mutable</i>.
     */
    public GeoTagMutable(GeoTag immutable_tag){
        super(immutable_tag.getLatitude(), immutable_tag.getLongitude(),
                immutable_tag.getTimestamp(), immutable_tag.getMessage()
        );
        this.mLatitude = immutable_tag.getLatitude();
        this.mLongitude = immutable_tag.getLongitude();
    }

    @Override
    public int getLatitude() {
        return mLatitude;
    }

    @Override
    public int getLongitude() {
        return mLongitude;
    }

    public void setLatitude(int latitude) {
        this.mLatitude = latitude;
    }

    public void setLongitude(int longitude) {
        this.mLongitude = longitude;
    }
}

The extending GeoTagMutable-class uses it’s own, non-final fields for latitude and longitude. It overrides the getLatitude() and getLongitude()-methods to return it’s own fields and offers two setter methods for them, too. Using the new class, we can now create new, mutable GeoTag objects and, we can change an existing, immutable object, to a mutable one:

GeoTag tag = new GeoTag(12, 14, new Date(), "Some message!");
GeoTagMutable mutableTag = new GeoTagMutable(tag);
mutableTag.setLatitude(41);
System.out.println(tag.getLatitude()+" > "+mutableTag.getLatitude());

To prevent this kind of “attack”, we declare the class to be final.

Mutable components in immutable objects

The above shown example is not completely immutable. It is possible to mutate the timestamp-field. See the following example “attack”:

Calendar calendar = Calendar.getInstance();
calendar.set(1999, 12, 24);
GeoTag tag = new GeoTag(12, 14, calendar.getTime(), "Some message!");
tag.getTimestamp().setDate(30);
tag.getTimestamp().setMonth(10);
System.out.println("GeoTag taken on: "+tag.getTimestamp().toLocaleString());

This gives us the following output:

GeoTag taken on: 30.10.1999 00:00:00

Congratulations. You just traveled back in time from christmas to halloween. But in terms of our “immutable” object, not that cool. The problem here is not our object (which is quite immutable), but the Date-object, which is not immutable.

To overcome this problem, we’ll need to make defensive copies of our “timestamp”.

Making defensive copies

As shown above, a mutable object can be changed, due to the fact that Java passes around object-references to method-calls (more on the “pass-by-reference” and “pass-by-value” stuff can be found here).

To overcome this, the getTimestamp()-method will now be implemented to use defensive copies. This is as easy as doing the following:

public Date getTimestamp() {
    return new Date(timestamp.getTime());
}

What happens here? Instead of giving out a reference to our internal Date-object, we create a new Date-instance (with the same time as our internal one) and pass that one out. This will preserve the original date, but protect us from the above explained “attack”. Run the code again and see for yourself:

GeoTag taken on: 24.12.1999 00:00:00

The timestamp is unchanged, because we didn’t give out our internal reference. So, now our GeoTag-class is save, right? Wrong!

Here is the new “attack”, suffering from the same problem:

Calendar calendar = Calendar.getInstance();
calendar.set(1999, 12, 24);
Date time = calendar.getTime();
GeoTag tag = new GeoTag(12, 14, time, "Some message!");
time.setDate(30);
time.setMonth(10);
System.out.println("GeoTag taken on: "+tag.getTimestamp().toLocaleString());

And once again, the output is:

GeoTag taken on: 30.10.1999 00:00:00

Our getTimestamp()-method might be secure now, but the Date-instance, given to our constructor, can still be manipulated (since we’re storing a reference to it). To prevent this attack, we’ll need to make a defensive copy in our constructor, too:

public GeoTag(int latitude, int longitude, Date timestamp, String message) {
    this.latitude = latitude;
    this.longitude = longitude;
    this.timestamp = new Date(timestamp.getTime()); // Defensive copy
    this.message = message;
}

Now, the first and the second attack show no effects. Our class is now truly immutable.

A quick word on performance: Yes, making defensive copies (which might happen quite often) can result in many object-creations (and therefor much GC-activity).

You should always use defensive copies when creating public APIs or libraries, which are most likely to be used by stupid programmers and bad people. When a component is used only internally (and you trust your users to use it correctly), it can be implemented to not make defensive copies. This should however be pointed out by the documentation for the specific method/object!

Don’t use clone() for defensive copies!

There is yet another possible “attack”, which can be used to manipulate the timestamp, before it is stored in the field. This “attack” uses the clone()-method of Date, which gets overloaded to manipulate the fresh cloned object. Here is the DateCloneAttack-class, which offers the manipulated clone()-method:

public class DateCloneAttack extends Date implements Cloneable{

    public DateCloneAttack(Date date){
        super(date.getTime());
    }

    /**
     * This method clones a {@code Date} and manipulates it.
     */
    @Override
    public Date clone(){
        Date result = (Date) super.clone();
        result.setDate(30);
        result.setMonth(10);
        return result;
    }
}

To make the attack work (and show why you shouldn’t use clone for defensive copies), let’s change the constructor to use the clone()-method of what seems to be a Date-object:

// Bad practice example. DON'T DO THIS!
public GeoTag(int latitude, int longitude, Date timestamp, String message) {
    this.latitude = latitude;
    this.longitude = longitude;
    this.timestamp = (Date) timestamp.clone();
    this.message = message;
}

Now, it’s possible to change the Date-object, which is passed to the constructor, after it is passed:

Calendar calendar = Calendar.getInstance();
calendar.set(1999, 12, 24);
DateCloneAttack attack = new DateCloneAttack(calendar.getTime());
GeoTag tag = new GeoTag(12, 14, attack, "Some message!");
System.out.println("GeoTag taken on: "+tag.getTimestamp().toLocaleString());

This will print out:

GeoTag taken on: 30.10.1999 00:00:00

The lesson to learn here is, that you can’t trust the user. If Date would have been final, creating a manipulated clone()-method would have been impossible. Again, if you only use the class internally and you trust your users, you can clone objects for defensive copying.

In an actual, real-live example, this attack could smuggle an invalid object around the validity tests and modify it afterwards, leaving the new object in an invalid state. Therefore, check vor validity after cloning or recreating your mutable parameters (on the defensive copies).

Although you might use a mix of both: In the constructor, recreate the object using it’s constructor. When giving the object out (in it’s setter-method), you can use clone(), because you already have a secured copy of it. But, since cloning objects does not necessarily result in any kind of performance-increase, but rather makes your code more error prone, you should not use clone() if possible.

Strings are immutable

You might wonder why we need to make a defensive copy of the Date, but not the String-object. This is, because String itself is immutable. Quoting from it’s JavaDoc:

Strings are constant; their values cannot be changed after they are created. String buffers support mutable strings. Because String objects are immutable they can be shared […]

A more detailed explanation might be found on the linked documentation page.

Conclusion

If possible, make a class immutable. It offers many upsides and almost no downside.

When doing so, there are five simple rules to create an immutable class. Quoting from “Effective Java - Second Edition”, Chapter 4, Item 15:

  1. Don’t provide any methods that modify the object’s state.
  2. Ensure that the class can’t be extended.
  3. Make all fields final.
  4. Make all fields private.
  5. Ensure exclusive access to any mutable components (defensive copies)

As a sixth rule: Document immutability. If you designed a component to be immutable, document so in the JavaDoc of this class.

Posted by Lukas Knuth

Comments

comments powered by Disqus