Sunday, March 04, 2012

Custom Java Immutable Object

All of us know String is Immutable and so are other wrapper class instances like Integer, Double etc. Here, we will primarily talk about how to create a custom Java immutable bean/object. We will start with basic bean and develop the concept as we move. So here is the most basic Java bean.

1
2
3
4
5
class Person {
        private String name;
        private int age;
        //getter and setter methods.
}

Now to make this class immutable all you have to do is to remove all the setter methods, add a public constructor which will set the values for name and age for the person and you are done. You have your first basic immutable class.

Here is the immutable person class

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
class Person {
        private String name;
        private int age;
        public Person(String name, int age){
                this.name = name;
                this.age = age;
        }

        public String getName(){
                return this.name;
        }

        public int getAge(){
            return this.age;
        }
}

Now once the Person object is constructed it cannot be changed. Reason being there are no setter for the attributes of Person and the attributes themselves are private, so you cannot change them.

The question now is what if the Person Class consisted of another member variable called address which is not an immutable object. Will the person class still continue to be an immutable class. Answer is no. Here is the reason.

Lets create an address class first.

1
2
3
4
5
6
7
8
9
class Address {
        private String city;
        private String country;
        public Address(String city, String country){
            this.city = city;
            this.country = country;
        }
        //getter and setter methods.
}


Assume that the one more attribute 'address' has been added to the person class and the constructor takes one more parameter of type Address. Below code shows how person class is no more a Immutable class.

1
2
3
4
5
6
7
8
9
class CheckImmutable{
        public static void main(String args[]){
                Address a = new Address("city", "country");
                Person p = new Person("test", 12, a); //According to above explanation p should be immutable.
                Address b = p.getAddress();
                b.setCity("cityChanged");
                System.out.println(p.getAddress().getCity());
        }
}

Output of this will be 'cityChanged'. So somehow we managed to change on of the attributes of Person class despite being the fact that person was an immutable class.
Immediate answer to this problem will be make changes to address class to make it immutable as well. What if Address class is in a third party API and you cannot change this class. Or may be Address class is being used in thousand other places in your code and you cannot afford to make changes everywhere. There a simple solution to this just by changing the Person class a bit.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
class Person {
        private String name;
        private int age;
        private Address address;
        public Person(String name, int age, Address address){
                this.name = name;
                this.age = age;
                this.address = new Address(address.getCity(), address.getCountry());
                //Create a new address object using the one provided as parameter
        }

        public String getName(){
                return this.name;
        }

        public int getAge(){
            return this.age;
        }

        public Address getAddress(){
                //Return a new address object instead of the one which is member of the Person object
                return new Address(address.getCity(), address.getCountry());
        }
}

What we have done here is while creating the person object using constructor, we have created a new Address object and assigned that in the attribute instead of using the one passed as parameter. Similarly, when returning the address object from the getAddress() method, we are creating a new address object and returning it back. Hence any class outside Person will never have access to the address which is inside the person object. Now if you again run the CheckImmutable class with the new Person class, you will see the address has not changed.

Is an instance of Person Class now Immutable. Answer is still a BIG "NO". And as expected the question is how. This time the properties of object person can be modified using Reflection.

Is there a way to stop that, if yes How. Answer to this is yes, using SecurityManager class in Java.
Let's not get into the details of SecurityManager now as this in itself calls for another round of discussion. So we will cover it some other day, in some other question dedicated to SecurityManager itself (well maybe).