Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why make defensive copies in getters inside immutable classes?

Tags:

java

This question is about good programming practices and avoiding potential holes.
I read Joshua Bloch's Effective Java and here is what I wonder:
Why should I consider making defensive copies in getter methods in my immutable class with no mutators in it?
And second: why should I make my fields final in addition to private ? Is this only about performance (not security) ?

like image 464
iozee Avatar asked Jun 08 '12 15:06

iozee


People also ask

What is defensive copying?

Defensive copying. A mutable object is simply an object which can change its state after construction. For example, StringBuilder and Date are mutable objects, while String and Integer are immutable objects. A class may have a mutable object as a field.

Why immutable objects do not require a copy constructor?

There is no need to ever make a copy of an immutable value object. Since it's immutable, both the original and copy will be equivalent for all time, and thus having two copies is nonsensical.

What is the advantage of making class immutable?

An immutable class is good for caching purposes because you don't have to worry about the value changes. Another benefit of immutable class is that it is inherently thread-safe, so you don't have to worry about thread safety in case of multi-threaded environment.

What is a disadvantage of immutable classes?

The only real disadvantage of immutable classes is that they require a separate object for each distinct value. Creating these objects can be costly, especially if they are large.


2 Answers

I believe this is the case that justifies this statements:

public class Immutable {

    private final String name;

    private Date dateOfBirth;

    public Immutable(String name, Date dateOfBirth) {
        this.name = name;
        this.dateOfBirth = dateOfBirth;
    }

    public String getName() {
        return name;
    }

    public Date getDateOfBirth() {
        return dateOfBirth;
    }

}

getName() is fine as it returns immutable object as well. However the getDateOfBirth() method can break immutability because the client code can modify returned object, hence modifying the Immutable object as well:

Immutable imm = new Immutable("John", new Date());

imm.getName(); //safe
Date dateOfBirth = imm.getDateOfBirth();
//hundreds of lines later
dateOfBirth.setTime(0);  //we just modified `imm` object

It is safe to return immutable objects and primitives (as they are returned by value). However you need to make defensive copies of mutable objects, like Date:

public Date getDateOfBirth() {
    return new Date(dateOfBirth.getTime());
}

and wrap collections in immutable views (if they are mutable), e.g. see Collections.unmodifiableList():

public List<Integer> getSomeIds() {
    return Collections.unmodifiableList(someIds);
}
like image 176
Tomasz Nurkiewicz Avatar answered Oct 20 '22 00:10

Tomasz Nurkiewicz


Object references in Java are promiscuous; if an object reference is passed to a method, that method will have no way of knowing who else might have a reference to that same object, or likewise what they might try to do with it. Likewise if a method returns an object reference, there's no telling what the recipient might do with it. Consequently, if a type allows anyone with a reference to mutate it, the only way an entity which holds a reference to such an object can ensure it won't be mutated is to keep a reference to a private object, which outside has never had, and will never get, a reference to.

An alternative to the style of defensive copying shown here (constructing a new object instance every time information is requested) is to have an object's information-retrieval method accept a mutable object and populate that object with the information that is retrieved. This approach requires that the code which is asking for the information construct a (likely blank) object to accept the information before calling the method to retrieve it, but if one will e.g. use a loop to retrieve, examine briefly, and discard 100 pieces of information, it may be faster to construct one object which can be reused every time through the loop, than to construct 100 new objects each of which will be used only briefly.

like image 43
supercat Avatar answered Oct 19 '22 22:10

supercat