Rules of Immutability

Estimated read time: 9 min

Originally published on July 17th, 2012 (Last updated on June 21st, 2020)

In a per­fect world, every val­ue con­tain­er (an object that only holds mul­ti­ple fields of data and defines meth­ods for access) is immutable. Immutabil­i­ty should always be a design-goal, espe­cial­ly, when cre­at­ing a library or API.

In this arti­cle, I’m going to explain what immutable objects are, why they are cool and what stum­bling blocks you should watch out for.

Although immutabil­i­ty, as a design-pat­tern, has no ori­gin in any par­tic­u­lar lan­guage, the fol­low­ing exam­ples (and most of the writ­ing) focus on the sit­u­a­tion in the Java pro­gram­ming lan­guage and might not hold for oth­er languages.

For exam­ples in oth­er pro­gram­ming lan­guages, the below linked Wikipedia arti­cle can be con­sult­ed, although it’s not that exten­sive as the fol­low­ing exposition.

What makes an object immutable”? #

Wikipedia boils it down to one sim­ple sentence:

In object-ori­ent­ed and func­tion­al pro­gram­ming, an immutable object is an object whose state can­not be mod­i­fied 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 encap­su­lates mul­ti­ple val­ues into one sin­gle object. It pro­vides geo­graph­i­cal infor­ma­tion for a mes­sage, includ­ing lat­i­tude and lon­gi­tude, the date of cre­ation and the mes­sage itself.

An instance of this class can only be cre­at­ed by using it’s con­struc­tor (which ini­tial­izes and pop­u­lates all fields with the giv­en val­ues). After the instance is cre­at­ed, there is no way to change any of those val­ues, since no set­ter-meth­ods are available.

By doing so, we can assure that this object can not be manip­u­lat­ed after it’s cre­ation. But what are the ben­e­fits of doing so?

Why immutabil­i­ty is cool #

As point­ed out above, the object can not be manip­u­lat­ed or changed in any way after it was cre­at­ed. This ensures the con­sis­ten­cy of the instance across the whole application.

You can access an immutable objects on mul­ti­ple threads simul­ta­ne­ous­ly, with­out the fear of any con­cur­rent mod­i­fi­ca­tion by anoth­er thread. Immutable objects are always syn­chro­nized. No extra work is needed.

This makes pass­ing objects around mul­ti­ple threads or across mul­ti­ple appli­ca­tion-mod­ules sim­ple. It’s the eas­i­est approach to thread-safety.

Also, immutable objects make for the per­fect Map-keys, because you don’t have to wor­ry about their val­ues (and there­for the key) chang­ing, which would destroy the Maps invariants.

Design­ing immutable objects #

In this sec­tion, I’m going to give some design-advices and show some pos­si­ble weak-spots.

Final­ize to freeze the state #

Let’s take a clos­er look. Notice how all the fields are declared to be private final? A field declared final can’t be assigned out­side of the constructor.

This secures any prim­i­tive val­ue-type as much as pos­si­ble. Since the prim­i­tive types don’t offer any meth­ods to mutate their state, they are now immutable.

Pre­vent extensibility #

We also declared the class itself to be final. Final­ized class­es can’t be extend­ed, which freezes their inter­nals from being manip­u­lat­ed. For exam­ple, an attack­er” could cre­ate a sub­class 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 extend­ing GeoTagMutable-class uses it’s own, non-final fields for lat­i­tude and lon­gi­tude. It over­rides the getLatitude() and getLongitude()-meth­ods to return it’s own fields and offers two set­ter meth­ods for them, too. Using the new class, we can now cre­ate new, muta­ble GeoTag objects and, we can change an exist­ing, immutable object, to a muta­ble 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 pre­vent this kind of attack”, we declare the class to be final.

Muta­ble com­po­nents in immutable objects #

But the GeoTag-class is not per­fect. It is pos­si­ble to mutate the timestamp-field. See the fol­low­ing exam­ple 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 fol­low­ing output:

Geo­T­ag tak­en on: 30.10.1999 00:00:00

Con­grat­u­la­tions. You just trav­eled back in time from christ­mas to hal­loween. But for our goal of immutabil­i­ty, not that cool. The prob­lem here is not our object (which is quite immutable), but the Date-object, which is not immutable.

To over­come this prob­lem, we’ll need to make defen­sive copies of our time­stamp”.

Mak­ing defen­sive copies #

As shown above, a muta­ble object can be changed, due to the fact that Java pass­es around object-ref­er­ences to method-calls (more on the pass-by-ref­er­ence” and pass-by-val­ue” stuff can be found here).

To over­come this, the getTimestamp()-method will now be imple­ment­ed to use defen­sive copies. This is as easy as doing the following:

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

What hap­pens here? Instead of giv­ing out a ref­er­ence to our inter­nal Date-object, we cre­ate a new Date-instance (with the same time as our inter­nal one) and return it. This will pre­serve the orig­i­nal date, but pro­tect us from the above explained attack”. Run the code again and see for yourself:

Geo­T­ag tak­en on: 24.12.1999 00:00:00

The time­stamp is unchanged, because we did­n’t give out our inter­nal ref­er­ence. So, now our GeoTag-class is save, right? Wrong!

Here is the new attack”, suf­fer­ing 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 out­put is:

Geo­T­ag tak­en on: 30.10.1999 00:00:00

Our getTimestamp()-method might be secure now, but the Date-instance, giv­en to our con­struc­tor, can still be manip­u­lat­ed (since we’re stor­ing a ref­er­ence to it). To pre­vent this attack, we’ll need to make a defen­sive copy in our con­struc­tor, 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 sec­ond attack show no effects. Our class is now tru­ly immutable.

A quick word on per­for­mance: Yes, mak­ing defen­sive copies (which might hap­pen quite often) can result in many object-cre­ations and there­for much GC-activ­i­ty. In gen­er­al how­ev­er, the cor­rect­ness of your pro­gram should out­weigh any per­ceived per­for­mance prob­lems. Always mea­sure the per­for­mance impact before con­cern­ing your­self with pos­si­ble per­for­mance issues prematurely!

Don’t use clone() for defen­sive copies! #

There is yet anoth­er pos­si­ble attack”, which can be used to manip­u­late the time­stamp, before it is stored in the field. This attack” uses the clone()-method of Date, which gets over­loaded to manip­u­late the fresh­ly cloned object:

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 should­n’t use clone for defen­sive copies), let’s change the con­struc­tor 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 pos­si­ble to change the Date-object, which is passed to the con­struc­tor, 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:

Geo­T­ag tak­en on: 30.10.1999 00:00:00

The les­son here is, that you can’t trust the user. If the Date-class would have been final­ized, cre­at­ing a manip­u­lat­ed clone()-method would have been impossible.

In an actu­al, real-live exam­ple, this attack could smug­gle an invalid object around the valid­i­ty tests and mod­i­fy it after­wards, leav­ing the new object in an invalid state. There­fore, check for valid­i­ty after cloning or recre­at­ing your muta­ble para­me­ters (on the defen­sive copies).

Although you might use a mix of both: In the con­struc­tor, recre­ate the object using it’s con­struc­tor. When giv­ing the object out (in it’s get­ter-method), you can use clone(), because you already have a val­i­dat­ed copy of it. But, since cloning objects does not nec­es­sar­i­ly result in any kind of per­for­mance-increase, but rather makes your code more error prone, you should not use clone() if pos­si­ble.

Strings are immutable #

You might won­der why we need to make a defen­sive copy of the Date, but not the String-object. This is, because String itself is immutable. Quot­ing from it’s JavaDoc:

Strings are con­stant; their val­ues can­not be changed after they are cre­at­ed. String buffers sup­port muta­ble strings. Because String objects are immutable they can be shared […]

A more detailed expla­na­tion might be found on the linked doc­u­men­ta­tion page.

Chang­ing a GeoTag #

An object that can’t be mod­i­fied is pret­ty bor­ing. What if you do have legit rea­sons to change cer­tain fields, for exam­ple if you want to make the mes­sage editable?

Let’s write a set­ter method for mes­sage that still leaves the object immutable:

public GeoTag setMessage(String newMessage) {
  return new GeoTag(getLatitude(), getLongitude(), getTimestamp(), newMessage);
}

The set­ter does not mod­i­fy the object but instead returns a new object with the mod­i­fied mes­sage. This means that ref­er­ences to the orig­i­nal GeoTag are still valid (and will still show the old mes­sage), but now there is a new object with the same latitude/​longitude/​timestamp and a new message.

Of course this means that any code that is cur­rent­ly work­ing with the old object will not see this change. But remem­ber that this is exact­ly what we want­ed, specif­i­cal­ly in a mul­ti-thread­ed con­text. In prac­tice, this is rarely a prob­lem because changes to an object require that oper­a­tions be re-run with the new object any­ways. For exam­ple, if our GeoTag was being per­sist­ed to a data­base, chang­ing the mes­sage would require per­sist­ing the change to the data­base again anyways.

Con­clu­sion #

Make your val­ue con­tain­ers immutable. It makes writ­ing cor­rect soft­ware much easier.

Here are the five sim­ple rules to cre­ate an immutable object in Java, tak­en from Effec­tive Java — Sec­ond Edi­tion”, Chap­ter 4, Item 15:

  1. Don’t pro­vide any meth­ods that mod­i­fy the objec­t’s state.
  2. Ensure that the class can’t be extended.
  3. Make all fields final.
  4. Make all fields private.
  5. Ensure exclu­sive access to any muta­ble com­po­nents (defen­sive copies)

As a sixth rule: Doc­u­ment immutabil­i­ty. If you designed a com­po­nent to be immutable, doc­u­ment so in the JavaDoc of this class.

Posted by Lukas Knuth

Comments

No com­ment sec­tion here 😄

You can reach me over at @knuth_dev or send me an Email.