Reputation: 223
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.
Upvotes: 2
Views: 154
Reputation: 32980
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;
}
}
Upvotes: 2
Reputation: 5233
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.
Upvotes: 3
Reputation: 21586
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!
)
Upvotes: 2
Reputation: 48682
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.
Upvotes: 1
Reputation: 2785
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.
Upvotes: 2