I know all the basic rules to make our class immutable but I am a little confused when there is another class reference. I know if there is collection instead of Address
then we can make use of Collections.unmodifiableList(new ArrayList<>(modifiable));
and then we can make our class immutable. But in below case I am still unable to get the concept.
public final class Employee{
private final int id;
private Address address;
public Employee(int id, Address address)
{
this.id = id;
this.address=address;
}
public int getId(){
return id;
}
public Address getAddress(){
return address;
}
}
public class Address{
private String street;
public String getStreet(){
return street;
}
public void setStreet(String street){
this.street = street;
}
}
You can also use shallow copy using cloning
Using this will create a separate object of Address in Employee class so in this case any changes made to the Address object passed as argument in Employee constructor will not change the member variable Address object of Employee class.
The getAddress() method is also returning a clone object so any changes made to the object fetched by this method do not effect the address object of Employee class.
Note: To use this make Address class Cloneable.
So in your example
Employee
class is immutable, because once it is created, you can't change its state, because it has only getter methods.Address
class ismutable
because you can modify it with thesetStreet
method.So if you have other class which uses
Address
object, you are sure that that class can't modify the objects state.If you want to encapsulate a mutable object into an immutable one, then you need to:
public Employee(int id, Address address){
Well there is steps provided by Java docs
Address class is mutable because you can modify it with the setStreet method. So other class can modify this class.
We can defend against this by taking a copy of the of the Address instance when it is passed in rather than trusting the reference to the instance we are given.
Making Address object final
Secondly,
Create constructor in Address class that sets Street.Remove setter method for street.
And finally instead of
Use
Well, the concept is reading the JLS and understanding it. In this case, the JLS says:
So you need to:
address
both final and private.In this case, #2 probably means you can't return a reference to Address like you have with
getAddress()
. And you have to make a defensive copy in the constructor. I.e., make a copy of any mutable parameter, and store the copy in Employee. If you can't make a defensive copy, there's really no way to make Employee immutable.Implementing
clone()
or something similar (a copy ctor) would make creating defensive objects easier for complicated classes. However, the best recommendation I think would be to makeAddress
immutable. Once you do that you can freely pass around its reference without any thread-safety issues.In this example, notice I do NOT have to copy the value of
street
.Street
is a String, and strings are immutable. Ifstreet
consisted of mutable fields (integer street number for example) then I would have to make a copy ofstreet
also, and so on ad infinitum. This is why immutable objects are so valuable, they break the "infinite copy" chain.