Immutability in Java either by your design (as seen in the previous posting) or as provided by the core libraries (as in the String class) is an all-or-nothing proposition. Immutability is determined by the design of the class itself, and not later while creating an object of this class. It’s all dandy if that’s what you want, but sometimes such rigidity gets your goat.
Consider a contact object that’s part of your person class:
class Contact{
private String email_address;
private String address;
//With getters and setters
}
class Validator{
static boolean isContactValid(Contact a, Contact b){
...
}
}
public class Person{
private Contact contactInfo;
private String name;
...
Person(String name){
this.name = name;
}
public setContact(Contact contact){
this.contactInfo = contact;
}
public getContact(){
return this.contactInfo;
}
...
public static void main(String args[]){
Person p = new Person("Calvin");
Contact c = new Contact();
//Set some values for this contact
p.setContact(c);
}
}
Should the contact object be immutable? Of course not. After all, people do tend to change their houses, phone number, email addresses etc.,.
Now what if you had to pass this contact information to another method? How can you ensure that the contact is not changed by that method i.e. how do you ensure that a method does not end up modifying the state of your object (probably inadvertently)?
Interestingly, this whole problem can be solved by one keyword in C++ – const. Adding const to a variable ensures that the values held in an object pointed to by that variable cannot be changed. So just declaring a variable as follows guarantees that the object referred to by that variable stays the same till the program terminates.
int main(){
Person* p = new Person("calvin");
const Person* q = p;
}
So in the above example, if you do a q->setName(“Hobbes”) it would be flagged by the compiler, but p ->setName(“Hobbes”) would be just fine. Simply put const-ness in C++ is an attribute attached to a variable and not to an object. This is one of the many myriad uses of const.
Sounds like time to use the keyword final for our variable in Java? Not so fast batman!
Adding the keyword final to a variable ensures that a particular reference cannot be changed again,it does not mean that the values pointed to by that reference are final (This is the equivalent of a const pointer in C++). What this means in the code snippet below, is that the variable obj cannot point to any other object but obj’s values can be changed. This of course applies only to higher level objects and not to primitives. A primitive once declared final cannot be changed.
final Person obj = new Person();
final int i = 10; // There is no way i can be changed
obj = new Person(); //Not allowed, a compilation error. Final always points to the same object till the end of time.
obj.setName("Calvin"); //This is fine, but this is what we want to make contextual
So what we need here is a mechanism that avoids an object’s value to be changed once it’s created. This does not necessarily mean that we need an immutable object. As I mentioned above under certain circumstances we want the ability to change the values in the Contact object but on certain other occasions we do not want the values to be touched.
Enter designing to an interface. An interface is a contract. When a class claims it has implemented an interface you immediately know the access points that class provides. In our preceding example, we would like to ensure that the isContactValid method doesn’t change any of the contact object’s properties. What we do now is to ensure that the Contact and Person be interfaces with only getters and their implementations provides mutable versions.
interface Contact{
String getEmailAddress();
String getAddress();
...
}
class ContactImpl implements Contact{
private String email_address;
private String address;
//The getter methods as declared in the interface
void setEmail(String email){
this.email_address = email;
}
...
}
class Person{
private String name;
private Contact c;
void setContact(Contact c){
this.c = c;
}
Contact getContact(){
return this.c;
}
public static void main(String args[]){
ContactImpl c = new ContactImpl();
c.setEmail("email@example.com");
...
Person p = new Person();
p.setContact(c);
Validate.isValidContact(c);
c = (ContactImpl) p.getContact();
}
}
What this does is that it exposes a immutable access point to your object and if a need arises you can very well cast it to the implementation and do the necessary mutating actions. You do not expose setter methods that might result in the object being changed. Now what is stopping isValidContact method from type casting to ContactImpl and wreck havoc? Well, nothing! But when you realize that you have to a type cast explicitly that should immediately ring a bell and lead to a rethink of your approach (hopefully!).
At this point no matter how many people vote, Sun is in no mood to add const to Java. So following this interface driven approach is one way to achieve limited const-ness in Java. Why do I say limited? Because I think there are things which can be achieved by using const that cannot be achieved in Java in a straight forward manner. One of them is the mechanism to ensure that a method doesn’t change any values. Or “codely” put how can we achieve the following in Java? Any ideas?
class Person{
private:
int age;
float pension;
public:
int getAge(){
return age;
}
float getPension() const{
if (age < 60)
return 0;
else{
//Some calculation stuff
age--; //BUG. This will be flagged an error during compilation. A probable logical bug that can be eliminated
return pension;
}
};