19

I have an immutable class called Employees like this:

public final class Employees {
    private final List<Person> persons;

    public Employees() {
        persons = new LinkedList<Person>();
    }

    public List<Person> getPersons() {
        return persons;
    }
}

How do I keep this class immutable?

I made the field private and final, and I didn't provide a setter method. Is this enough to achieve immutability?

smci
  • 32,567
  • 20
  • 113
  • 146
Szanownego
  • 239
  • 1
  • 11
  • "is it enough to achieve that?" - No, because I could still call `getPersons().add( somePerson )`. – JimmyB Oct 07 '16 at 08:43
  • No. Not by a long shot. – Boris the Spider Oct 07 '16 at 14:27
  • Very related http://stackoverflow.com/questions/6137224/immutable-object-with-arraylist-member-variable-why-can-this-variable-be-chang and http://stackoverflow.com/questions/9006040/immutability-of-a-class-when-an-instance-variable-present-as-arraylist and http://stackoverflow.com/questions/24984890/objects-in-an-immutable-class-as-member-variable – Tunaki Oct 07 '16 at 16:24
  • Is `Person` mutable? – jpmc26 Oct 07 '16 at 19:38

6 Answers6

18

Answer edited to explain not only the case with a mutable version of Person, but also with an immutable version of Person.


Your class is mutable because you can do that:

Employees employees = new Employees();
employees.getPersons().add(new Person());

Note that not passing a list of persons to the constructor if you change your code to create an immutable class you will have a not useful class holding an ever empty list of persons, so I assume that is necessary to pass a List<Person> to the constructor.

Now there are two scenarios, with different implementations:

  • Person is immutable
  • Person is mutable

Scenario 1 - Person is immutable

You need only to create an immutable copy of the persons parameter in the constructor.

You need also to make final the class or at least the method getPersons to be sure that nobody provide a mutable overwritten version of the getPersons method.

public final class Employees {
    private final List<Person> persons;

    public Employees(List<Person> persons) {
        persons =  Collections.unmodifiableList(new ArrayList<>(persons));
    }

    public List<Person> getPersons() {
        return persons;
    }
}

Scenario 2 - Person is mutable

You need to create a deep copy of persons in the getPersons method.

You need to create a deep copy of persons on the constructor.

You need also to make final the class or at least the method getPersons to be sure that nobody provide a mutable overwritten version of the getPersons method.

public final class Employees {
    private final List<Person> persons;

    public Employees(List<Person> persons) {
        persons = new ArrayList<>();
        for (Person person : persons) {
            persons.add(deepCopy(person));   // If clone is provided 
                                           // and creates a deep copy of person
        }
    }

    public List<Person> getPersons() {
        List<Person> temp = new ArrayList<>();
        for (Person person : persons) {
            temp.add(deepCopy(person)); // If clone is provided 
                                         // and creates a deep copy of person
        }  
        return temp;
    }

    public Person deepCopy(Person person) {
        Person copy = new Person();  
        // Provide a deep copy of person
        ...
        return copy;
    }
}

This part of the answer is to show why a not deep copy of the personsParameter passed to the constructor can create mutable versions of Employees:

List<Person> personsParameter = new ArrayList<>();
Person person = new Person();
person.setName("Pippo");
personsParameter.add(person);
Employees employees = new Employees(personsParameter);

// Prints Pippo    
System.out.println(employees.getPersons().get(0).getName()); 


employees.getPersons().get(0).setName("newName");

// Prints again Pippo    
System.out.println(employees.getPersons().get(0).getName()); 

// But modifiyng something reachable from the parameter 
// used in the constructor 
person.setName("Pluto");

// Now it prints Pluto, so employees has changed    
System.out.println(employees.getPersons().get(0).getName()); 
georgeliatsos
  • 1,168
  • 3
  • 15
  • 34
Davide Lorenzo MARINO
  • 26,420
  • 4
  • 39
  • 56
  • 1
    Well, if you are allowed to change the original Object then yes, you are right, we will need defensive copies. What i am trying to say is if `Person` class ensures that it is immutable, then the container (`List`) doesn't have to worry about `Person'`s immutablility. All it has to worry about is ensuring that only the List is immutable. when nobody can change the state of an object, the same object can be passed around :) – TheLostMind Oct 07 '16 at 09:20
  • @Davide Lorenzo MARINO - Thanks for your reply, it is very clear. I would like to add one thing, I checked Defensive Copies Item on Joshua Block's "Effective Java". he says this "clone method [...] could return an instance of an untrusted subclass specifically designed for malicious mischief. [...] Do not use the clone method to make a defensive copy of a parameter whose type is subclassable by untrusted parties ". So to ensure efficient copy, it's better not to use Clone, but copying all fields of Person manually, in your example. – Szanownego Oct 07 '16 at 12:40
  • You can use the clone of a custom class like Person without problems because is you the author of the clone method. – Davide Lorenzo MARINO Oct 07 '16 at 12:42
  • 2
    @DavideLorenzoMARINO in which case, it is better to write a copy ctor or `static` copy method - `clone()` has **massive** issues; being the author of the method does not solve this as you still need to call `super.clone()` and those issues are still present. I was tempted to downvote this answer just for the use of `clone()` in fact; but decided against. What this answer really misses is a simple explanation - "a `class` is immutable iff it and all of its members are immutable". So if `Person` were immutable, the whole `clone()` nonsense could be avoided. – Boris the Spider Oct 07 '16 at 15:00
  • @BoristheSpider yes, assuming that Person is an immutable class simplify a lot all. Not only the clone() (or equivalent) is not necessary, but also copy of the List persons is not necessary if you take care creating it as an immutable list. Basically in this case you need only to create the immutable List persons in the constructor. – Davide Lorenzo MARINO Oct 07 '16 at 16:44
  • You do not need a deep clone unless `Person` is mutable. Additionally, the OP "being the author of `Person`" does not make `clone` safe. That class must be *designed* such that clone is safe. In particular, this means that it must be implemented either using only primitive instance variables (unlikely to stay that way) or then deep clone all its own data. It would generally just be better to make `Person` immutable (or create an immutable variation) than worry about all that. -1 – jpmc26 Oct 07 '16 at 19:38
  • @jomc26 and to all - To be more precise I added both scenarios, with a mutable version of Person and with an immutable version of `Person`. Additionally, assuming also that `Person` is not under the control of the programmer I added the `deepCopy` method explicitly instead of propose the `clone`. – Davide Lorenzo MARINO Oct 08 '16 at 07:38
  • @DavideLorenzoMARINO In the case Person is Mutable, why in the get method you create a new copy of the instance field? Can't we return simply the copy created in the constructor? – Szanownego Feb 05 '17 at 14:38
  • @Szanownego if Person is mutable returning directly the persons inside the List you can modify the Person itself so you will change the Employees content. Basically you can do something like employees.getPersons().get(0).setName("New name"); this will change the content of employees. – Davide Lorenzo MARINO Feb 06 '17 at 08:22
17

No, it is not enough because in java even references are passed by value. So, if your List's reference escapes ( which will happen, when they call get), then your class is no longer immutable.

You have 2 choices :

  1. Create a defensive copy of your List and return it when get is called.
  2. Wrap your List as an immutable / Unmodifiable List and return it (or replace your original List with this, then you can return this safely without further wrapping it)

Note : You will have to ensure that Person is either immutable or create defensive copies for each Person in the List

TheLostMind
  • 35,966
  • 12
  • 68
  • 104
  • If person is mutable, and a list is passed to the constructor as parameter you need to create also a defensive copy of it in the constructor. Making a defensive copy in the getter is not enough. Also wrapping in an immutable list is not enough if person is mutable. – Davide Lorenzo MARINO Oct 07 '16 at 09:03
  • @DavideLorenzoMARINO - What the OP has to ensure is that all transitively reachable objects are either immutable or take care of returning defensive copies – TheLostMind Oct 07 '16 at 09:05
  • no as I said is not enough if a mutable parameter is passed to the constructor and no deep defensive copy of it is done on the constructor. I try to edit my answer to show that. – Davide Lorenzo MARINO Oct 07 '16 at 09:07
  • @DavideLorenzoMARINO - if I get a reference of a object which has no public fields and setters, how can I modify it?. Every composed Object ahs to ensure that it is immutable OR the parent containing a mutable object has to ensure that it returns defensive copies – TheLostMind Oct 07 '16 at 09:09
  • @TheLostMind - Can you Explain me better the second solution, what does it mean "Wrap your List as an immutable / Unmodifiable List"? – Szanownego Oct 07 '16 at 10:03
  • 4
    @Szanownego - After the `List` is constructed. Use `persons = Collections.unmodifiableList(persons)`. And in your `getPersons` return `persons` – TheLostMind Oct 07 '16 at 10:10
8

The answer is found in the docs - A Strategy for Defining Immutable Objects:

  1. Don't provide "setter" methods — methods that modify fields or objects referred to by fields.

  2. Make all fields final and private.

  3. Don't allow subclasses to override methods.

  4. If the instance fields include references to mutable objects, don't allow those objects to be changed:

    4.1. Don't provide methods that modify the mutable objects.

    4.2. Don't share references to the mutable objects. Never store references to external, mutable objects passed to the constructor; if necessary, create copies, and store references to the copies. Similarly, create copies of your internal mutable objects when necessary to avoid returning the originals in your methods.

Maroun
  • 94,125
  • 30
  • 188
  • 241
6

You could do even better with

import java.util.Collections;
...
public List<Person> getPersons() {
    return Collections.unmodifiableList( persons);
}
Tobias Otto
  • 1,634
  • 13
  • 20
  • This is mutable because you can mutate a single person. myInstance.getPersons().get(0).setName("pippo"); – Davide Lorenzo MARINO Oct 07 '16 at 08:47
  • 2
    @DavideLorenzoMARINO -But that cannot be prevented from here. Don't provide setters in `Person` then. – TheLostMind Oct 07 '16 at 08:52
  • @TheLostMind is not enough don't provide setters on Person. If Person is not unodifiable you can have always a possibility to change Employees – Davide Lorenzo MARINO Oct 07 '16 at 08:54
  • @DavideLorenzoMARINO - Well yes, either the OP can created defensive copies of all elements of the List or ensure that an existing Person cannot be modified by using constructor injection and no setters. – TheLostMind Oct 07 '16 at 08:56
  • @TheLostMind Is not enough. If you pass a mutable variable to the constructor and you don't make a deep copy of it in the constructor, also not exposing setters you can modify the original parameter passed to the constructor. – Davide Lorenzo MARINO Oct 07 '16 at 08:59
4

In this case I would favour not exposing the List or Map at all. If you pass out the collection are you breaking encapsulation by assuming it must be stored as a List.

If you don't expose the List, you can avoid needing to wrap it or provide a defensive copy.

public final class Employees {
    private final List<Person> persons;

    public Employees() {
        persons = new LinkedList<Person>();
    }

    public Person getPerson(int n) {
        return persons.get(n);
    }  

    public int getPersonCount() {
        return persons.size();
    }
}

The reason you want to avoid defensive copies is they can be called a lot. Someone could easily write (you would be surprised how often this is written)

for (int i = 0; i < employees.getPersons().size(); i++) {
    Person p = employees.getPersons().get(i);

}

By giving specific methods you can optimise the code for that use case.

Peter Lawrey
  • 525,659
  • 79
  • 751
  • 1,130
  • Wouldn't it make more sense to require an `Employees` instance to be initialized with a set of `Person` ? – Spotted Oct 07 '16 at 09:53
  • 2
    Actually, this is a good option. This way we only have to ensure that `Person` is either immutable / defensive copy is returned – TheLostMind Oct 07 '16 at 10:14
0

I think the problem with that approach is that I still can do getPersons().add(x). For not allowing that, you have to create a constructor that receives the list of persons and then instantiate your internal final list as, for example, ImmutableList. The rest of the code seems alright for me.

EDIT:

as Davide Lorenzo MARINO points, this is not enough. You also would need to return a deep copy of the list in the getter, so users can't modify your internal list by the getter returned reference.

By doing this you can just have a normal LinkedList as your itnernal List (private final). On your constructor you create a deep copy of the List and on your getter you return a deep copy of your internal List.

Rubasace
  • 939
  • 8
  • 18
  • Is not enough. You need to be sure that all that can be reached from the list must be copied or immutable. Basically you need to make a deep copy both in the constructor and in the getter. – Davide Lorenzo MARINO Oct 07 '16 at 09:04