Java private class fields not so private
Brushing up on some Java I came across an interesting “feature” of private class fields that reference mutable objects.
I thought it might help someone if I show how to get round the issue.
In order to illustrate this problem we first need a basic (very basic) class:
class Person {
Person(int year, int month, int day) {
GregorianCalendar calendar = new GregorianCalendar(year, month, day);
this.dateOfBirth = calendar.getTime();
}
public Date getDateOfBirth() {
return dateOfBirth;
}
private Date dateOfBirth;
}
So apart form its limited functionality what is wrong with this class? It has a private class field and an accessor method for it so in theory it is nice and safe encapsulated in the class right? Wrong!
The class field is a references to an object. When the accessor method is called it returns a copy of the reference to that object. Now the class and the code that called the accessor method both have a reference to the same object. This means that the code that called the accessor method can change the object and the Person class can do nothing about it.
Here is a little example, first a Person object is created:
Person aPerson = new Person(1958, 1, 5);
A call to getDateOfBirth() would return a reference to a Date object with a year of 1958. All good so far.
Now for the mayhem; The code below creates a new Date object called newDateOfBirth. It is a copy of the Person’s dateOfBirth object reference. 10 years in milliseconds is calculated and added to newDateOfBirth in effect bringing the newDateOfBirth forward 10 years.
Date newDateOfBirth = aPerson.getDateOfBirth();
double tenYearsInMilliSeconds = 10 * 365.25 * 24 * 60 * 60 * 1000;
newDateOfBirth.setTime(newDateOfBirth.getTime() + (long) tenYearsInMilliSeconds);
Because newDateOfBirth is a new variable it is reasonable to think that it is a new object on its own and has nothing to do with the Person object. The problem here though is because a reference was passed to it newDateOfBirth and aPerson.dateOfBirth are both looking at the same object. Any change made to newDateOfBirth will effect aPerson.dateOfBirth. The following code proves this:
System.out.println(aPerson.getDateOfBirth());
This will give the year 1968 not 1958.
A simple way to fix this problem is to return a reference to a copy of the object instead of the reference itself. This is a simple change to the getDateOfBirth method:
public Date getDateOfBirth() {
return (Date) imutabledateOfBirth.clone();
}
By using the clone method this problem is fixed, smily faces all round.
No Comments Yet