Mutable Accessors
Understanding the difference between mutable and immutable objects in Java is easy. Gaining access to both mutable and immutable objects is also easy … however methods accessing mutable objects require special attention. In every IDE I have used, it’s possible to generate public accessors (IE. getters and setters) for private fields. Most of the time, these methods are correct and save you some rather boring and error-prone typing. But most certainly not all the time.
The difference between a mutable object and an immutable object is when you have a reference to an instance of a mutable object, the content of that instance can be altered. With immutable objects the content of an instance can not be altered. An example frequently used to illustrate this is the immutable String object. Should you require more information on the difference between mutable and immutable objects, I suggest Wikipedia.
When you generate getters for mutable objects, it’s important to stay focused as your IDE will likely generate methods that are not always entirely correct.
Assume you have a class called TestMutable.java:
import java.util.Calendar; import java.util.GregorianCalendar; public class TestMutable { private Calendar myCalendar; public Calendar getMyCalendar() { return myCalendar; } public void setMyCalendar(Calendar myCalendar) { this.myCalendar = myCalendar; } }
All you need to do, is declare a private mutable object (myCalendar) and let your IDE generate the getter and setter. At first sight, all looks correct. Add the following:
import java.text.DateFormat; import java.text.SimpleDateFormat; import java.util.Date; public static void main(String[] args) { TestMutable testMutable = new TestMutable(); testMutable.setMyCalendar(new GregorianCalendar()); System.out.println("myCalendar: " + readCalendar(testMutable.getMyCalendar())); Calendar myNewCalendar = testMutable.getMyCalendar(); myNewCalendar.add(Calendar.YEAR, 1); System.out.println("myCalendar: " + readCalendar(testMutable.getMyCalendar())); } private static String readCalendar(Calendar cal) { DateFormat euroDate = new SimpleDateFormat("dd-MM-yyyy"); return euroDate.format(new Date(cal.getTimeInMillis())); }
I added readCalendar to make it more readable, however it also works with that method. If you don’t want it, you can also remove the extra imports. If you execute this code, you will receive this result:
myCalendar: 26-02-2009 myCalendar: 26-02-2010
myCalendar has changed, without using it’s setter, because it’s a mutable object! That is obviously not what we want and might cause problems in your application. To solve this problem, change the auto-generated getter of myCalendar to:
public Calendar getMyCalendar() { return (Calendar)myCalendar.clone(); }
If you execute the new code, myCalendar will not have changed: (Calendar.clone() returns Object, instead of Calender, hence the cast. This is called a covariant return)
myCalendar: 26-02-2009 myCalendar: 26-02-2009
The complete example:
import java.text.DateFormat; import java.text.SimpleDateFormat; import java.util.Calendar; import java.util.Date; import java.util.GregorianCalendar; public class TestMutable { private Calendar myCalendar; public static void main(String[] args) { TestMutable testMutable = new TestMutable(); testMutable.setMyCalendar(new GregorianCalendar()); System.out.println("myCalendar: " + readCalendar(testMutable.getMyCalendar())); Calendar myNewCalendar = testMutable.getMyCalendar(); myNewCalendar.add(Calendar.YEAR, 1); System.out.println("myCalendar: " + readCalendar(testMutable.getMyCalendar())); } public Calendar getMyCalendar() { return (Calendar)myCalendar.clone(); } public void setMyCalendar(Calendar myCalendar) { this.myCalendar = myCalendar; } private static String readCalendar(Calendar cal) { DateFormat euroDate = new SimpleDateFormat("dd-MM-yyyy"); return euroDate.format(new Date(cal.getTimeInMillis())); } }
An even better way to solve this problem is to write a final immutable Calendar class. However I have yet to be confronted with an application where the overhead caused by the clone and the cast, is a real issue.
If your application presents strange behaviour and you suspect it might be due to mutable objects, I highly recommend FindBugs. FindBugs will find these problematic getters with the following text:
[M V EI] May expose internal representation by returning reference to mutable object [EI_EXPOSE_REP]
FindBugs has helped me in the past to locate bugs. If you haven’t already, give it a try.