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

There are no comments yet. You could be the first!

Leave a Comment