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).