Handling mutable fields in Java

In Java, a mutable object is one whose state can be altered after it has been instantiated. An immutable object is one whose state is fixed after instantiation; that is, the data represented by the object cannot be changed in that object. Perhaps the most well-known immutable type is the built in String class; while there are methods on the String class that seemingly alter its state (such as toUpperCase() and trim()), in actuality these methods return a new String object if changes had to be made. In this article I’ll discuss how mutability will affect how you expose private fields in objects.

Pop Quiz

Consider the following code fragment. We create a MapContainer object, and then get the contained map, which is guaranteed to have a certain value associated with the key “today”. We then alter the value associated with this key, using our local reference to returned map. We then query the MapContainer object and get the contained map again. What is the value associated with the key “today” in this map?

final MapContainer mapContainer = new MapContainer();
final Map<String, String> map = mapContainer.getKeyValuePairs();

final String today = map.get("today");
assert null != today;
System.out.println(today);  // Returns the current date-time.

// Change the value using our local reference.
map.put("today", "tomorrow");

final Map<String, String> mapAgain = mapContainer.getKeyValuePairs();
System.out.println(mapAgain.get("today")); // What is output?

Don’t waste too much time on this problem, as it’s a trick question. The answer actually depends on the implementation of MapContainer. Depending on how it’s implemented, the second output could be unchanged from the first or be changed to the new value of “tomorrow”.

It’s all in the getters

Let’s take a look at the code for MapContainer.

import java.util.Date;
import java.util.HashMap;
import java.util.Map;

public class MapContainer
{
  final private Map<String, String> keyValuePairs;

  public MapContainer()
  {
    this.keyValuePairs = new HashMap<String, String>();
    this.keyValuePairs.put("today", new Date().toString());
  }

  public Map<String, String> getKeyValuePairs()
  {
    return keyValuePairs;
  }
}

We have a simple constructor that initializes the keyValuePairs Map and adds one value for the current date-time. But the real ‘key’ (no pun intended) to solving the problem is looking at the getter for the field. As you can see, it simply returns a reference to the private field. Under this implementation, a caller is able to alter the contents of the private field/Map even though no public “set” methods are available. Why is this? For two reasons: In Java, objects are passed/returned by reference, and HashMap is a mutable object. Thus using this implementation, the second output from our original code fragment is “tomorrow”, since the caller has altered the contents of the Map through the returned reference.

Furthermore, the original reference returned from the getter is not independent either; if some other code were to call the get method on the MapContainer object and make changes to the Map, those changes would also be reflected in the original returned reference!

How can we “fix” this? We simply have to ensure that the getter for the field returns a reference to a copy of the private Map. This is easy since there is a constructor for HashMap that accepts an existing Map. Here’s the altered code:

import java.util.Date;
import java.util.HashMap;
import java.util.Map;

public class MapContainer
{
  final private Map<String, String> keyValuePairs;

  public MapContainer()
  {
    this.keyValuePairs = new HashMap<String, String>();
    this.keyValuePairs.put("today", new Date().toString());
  }

  public Map<String, String> getKeyValuePairs()
  {
    return new HashMap<String, String>(keyValuePairs);
  }
}

With these changes, the private Map cannot be altered by a caller and thus the second output will remain changed in our first code fragment example.

To change, or not to change?

It should be noted that sometimes you may want to allow callers to alter the backing data structure that you return from a get method. For example, some of the data structures from the Java Collection Framework have getters that return references that can be used to alter the state of the original object. A good example is the entrySet() method of the HashMap object.

But in my opinion, these examples are the exception rather than the rule. In general, you do not want to allow callers to be able to alter the state of private fields directly since this violates information-hiding principles. If there is some change a caller needs to make to your object, it’s best accomplished through a set method since this allows you to control the changes and prevents unwanted/unexpected situations. If you do decide to allow callers to directly alter the state of private fields, it’s best to explicitly document this in the JavaDoc.

Mutability and safety

Note that in this example the field used was a HashMap object, which was mutable. If the field consisted of an immutable object, like a String, you would not have to worry about making a copy before returning it. This is because if the object is immutable, you do not have to worry about a caller changing its state because this is impossible to do! This is why immutable objects are much easier to deal with in multithreaded/concurrent environments.

Note that mutability has nothing to do with the final keyword in Java, contrary to this definition. Simply marking a field as “final” will not magically change a mutable object into an immutable one. As we saw earlier, whether an object is mutable or not depends entirely on its implementation, the details of which should be expressed in the JavaDoc for that class. The final keyword only ensures that you cannot reassign that field/variable to completely new reference or object; it does not ensure that you can’t change the state of the object already referenced.

Comments for this entry are closed

But feel free to indulge in some introspective thought.