2

For instance, I've got this:

public class Container{

    private final List<String> strs;

    /**
    * Contructs {@code Container} by a given {@code List}
    * The content of the list referenced should not be modified 
    * after the constructor invocation.
    */
    public Container(List<String> strs){
        this.strs = strs;
    }
    //Other staff
}

The state of Container could be modified after its construction, but we prohibit in the documentation. Can the object be treated as immutable? Making copy within the constructor is not exactly desirable.

The immutability is for thread-safety sakes.

Alupkers
  • 223
  • 1
  • 9
  • 1
    It's not good to rely on comments to guide programmer's choices, because we don't always read comments. More often, we rely on sample code and intuition to decide what can be done with an object. – Adi Levin Jan 22 '16 at 12:51
  • 3
    "Making copy within the constructor is not exactly desirable.". Well, but if you don't you cannot claim to be immutable. – Thilo Jan 22 '16 at 12:54
  • Personally, I would make the copy in the constructor. That gives me a List that I can guarantee will not add, reorder or lose elements. But if it should be a deep copy or not (i.e. if the elements themselves should be immutable) depends on the case. – Thilo Jan 22 '16 at 12:58
  • 1
    "invocation" ("c" not "k") – T.J. Crowder Jan 22 '16 at 12:58
  • 1
    It seems to me the question is not whether we _can_ - of course we _can_ - it's whether we _should_. – davmac Jan 22 '16 at 13:04
  • An alternative to making the copy would be to pull in a library that supplies immutable lists (like Guava) and accept only those in your constructor. Then you get type-safe immutability. – Thilo Jan 22 '16 at 13:05

5 Answers5

3

If you don't want to copy the list, and still you want it to be really immutable, you can use a persistent data structure such as the ones in Clojure.

In Clojure, all values are immutable, so you can always pass them around safely. When someone adds an item to the front of a list, this logically creates a new list, but actually it only adds a single element that points to the head of the old list, without having to copy all of the previous list. All data structures in Clojure are like that, and you can also use them in Java (see also what's a good persistent collections framework for use in java?).

Or, you can use pcollections.

Community
  • 1
  • 1
Adi Levin
  • 5,165
  • 1
  • 17
  • 26
  • 1
    A couple more library choices discussed at http://stackoverflow.com/questions/7713274/java-immutable-collections – Thilo Jan 22 '16 at 13:08
2

You should in general not expose internal structures of classes to the outside. This is espacially important for imutables.

Therefore I suggest to make a copy with new ArrayList(x) in your constructor.

In addition you can use Collections.unmodifiableList() to prevent modifications from inside your class.

I suggest the combination of both, which will look like this:

import java.util.Collections;
...

public Container(List<String> strs){
    this.strs = Collections.unmodifiableList(new ArrayList<>(strs));
}

Your container will remember the members of the list at the time of the call of the constructor. If the list is changed somewhere else, it will not have any effect on your imutable.

Even the code within the container will not be able to modify the list - the list would throw an UnsupportedOperationException instead.

The complete, working example code:

import java.util.ArrayList;
import java.util.Collections;
import java.util.List;

public class X {

    public static void main(String[] args) {

        // create a list
        List<String> myList = new ArrayList<>();
        myList.add("a");
        myList.add("b");

        // hand it over to the container
        Container container = new Container(myList);

        // modify it afterwards
        myList.add("BUH!");

        // check contents of container
        for (String item : container.strs) {
            System.out.println(item);
        }
    }

}

class Container{

    final List<String> strs;

    /**
    * Contructs {@code Container} by a given {@code List}
    * The content of the list referenced should not be modified 
    * after the constructor invokation.
    */
    public Container(List<String> strs){
        this.strs = Collections.unmodifiableList(new ArrayList<>(strs));
    }
    //Other staff
}

it outputs:

a
b

(does not output BUH!)

slartidan
  • 20,403
  • 15
  • 83
  • 131
  • Well, if you make a copy, you don't need to do `unmodifiableList` anymore (unless you want to protect yourself from yourself). – Thilo Jan 22 '16 at 13:02
2

You can just document it to improve performance, but I'd only recommend that approach for non-public APIs. In my company we built a framework and manage to take off some of these protections to boost performance/decrease memory usage, but only in code that was internal. We decided to assume that the framework developers wouldn't mess around and it worked well.

If your API is going to be exposed to other developers outside you should always be safe, specially for security reasons. If you really need to improve performance/memory usage, it is an option, but apply it carefully.

Daniel Pereira
  • 2,720
  • 2
  • 28
  • 40
2

Your decision to allow such a construct is a trade-off between guaranteed correctness and performance.

The builder pattern may give you a third way to handle this (depending if it is applicable to your construction of the list):

To construct the Container create a Builder which collects the list elements and then finally invokes the (private) Container constructor. This allows convenient collection of the list elements and avoids copying the list when constructing the final object:

public class Container {
     public static class Builder {
         public Builder add(String s) { strs.add(s); return this; }
         public Container create() { 
               Container c = new Container(Collections.unmodifiableList(strs)); 
               strs = null; 
               return c; 
         }
         private List<String> strs = new ArrayList<>();
     }

     private final List<String> strs;

     private Container(List<String> strs){
          this.strs = strs;
     }
}
wero
  • 32,544
  • 3
  • 59
  • 84
  • Note how it sets `strs = null` after constructing the instance, so that the caller cannot just continue building after the list has been used in construction. That way it is guaranteed to not change anymore. (The call to unmodifiableList is not really needed, only protects your Container from itself). – Thilo Jan 22 '16 at 13:23
1

In general, no. An immutable object can be safely shared between threads. An object being safe to share between threads is not a mere matter of documentation; its class must be implemented in a specific way. Say "objects of this class are immutable" without an implementation that made it immutable would be actively misleading, an untruth, and should be treated as a bug.

Raedwald
  • 46,613
  • 43
  • 151
  • 237
  • 2
    Actually, the class above would be problematic even with a single thread (as the contents of the list can change, which would be weird, even if it does not lead to an "internally inconsistent" list itself -- which could happen with multiple threads only). – Thilo Jan 22 '16 at 13:00