I am trying to determine what the best practices would be for an ImmutableList. Below is a simplistic example that will help drive my questions:
Ex:
public ImmutableCollection<Foo> getFooOne(ImmutableList<Foo> fooInput){
//.. do some work
ImmutableList<Foo> fooOther = // something generated during the code
return fooOther;
}
public Collection<Foo> getFooTwo(List<Foo> fooInput){
//.. do some work
List<Foo> fooOther = // something generated during the code
return ImmutableList.copyOf(fooOther);
}
public void doSomethingOne(){
ImmutableCollection<Foo> myFoo = getFooOne(myList);
...
someOtherMethod(myFoo);
}
public void doSomethingTwo(){
Collection<Foo> myFoo = getFooOne(myList);
...
someOtherMethod(myFoo);
}
My Questions:
Which makes the most sense to use in an application? [doSomethingOne and getFooOne] or [doSomethingTwo and fooTwo]? In other words if you know you are using ImmutableCollections does it make sense to keep casting back and forth and doing copyOf(), or just use Immutable everywhere?
These examples are public methods which could imply that other people use them. Would any of these answers change if the methods were private and used internally?
If a user tries to add anything to an immutable List an exception will be thrown. Because they may not be aware of this, wouldn't it make more sense to explicitly return an ImmutableCollection instead of a Collection?
In general, it's wise not to commit to a specific implementation in your declared return type, but we think of the immutable types as an exception. There are a few reasons to declare a return type of
Immutable*
:null
.asList()
orreverse()
method.copyOf()
call if he wishes to assign to anImmutable*
field. (But note that, if he does includecopyOf()
, it will short-circuit for most immutable inputs, even if you don't declare the return type.)Basically, I'm just cribbing from https://github.com/google/guava/wiki/TenThingsAboutImmutableCollections, which you may want to check out in its entirety.
This makes no sense at all. Why would you ever copy an immutable collection? The whole point of immutability is: it can't be changed, so you might as well re-use it.
??? Why do it twice??? This is fine:
ImmutableList.copyOf(Collection)
is smart enough to returnImmutableList
unmodified and create a newImmutableList
for everything else.My usual approach is:
accept List for parameters (so the interface is easier to use for clients)
if performance/memory usage/thread-safety is important, copy the contents of the provided List into a data structure that is optimized for usage by your class
when returning an ImmutableList, ImmutableList should be the return type (because it gives the caller more information about how it can use the returned value)
when returning a mutable implementation of List, List should be the return type, unless something else about the return type is important (thread-safety, as a bad* example)
* It's a bad example because if your return values need to be thread-safe, it probably means something else is wrong with your code.
Replace List/ImmutableList with any of the immutable collection types.
You should always use the standard JRE classes on public interfaces. There are no extra methods on Guava's
Immutable...
classes so you're not gaining any compile-time safety: any attempts to make changes to those objects will only fail at run-time (but see Bart's comment). You should document in methods that return collections that they're immutable.You should make defensive copies of lists provided on public methods if you're worried about concurrent modification, but it's OK to specify
ImmutableCollection
on private method arguments.If I understood your intentions, the proper way of designing
getFooXxx
for making an immutable copy of maybe-mutable-list is something like this:Why?
ImmutableList.copyOf()
does it's magic when given list is immutable,ImmutableList
which is, in fact, anImmutableCollection
but why would you like to hide information aboutImmutableList
from user? If he wants, he'll writeIterable foo = immutableCopyOfFoo(mutableFoo);
instead, but 99% he'll use an ImmtableList,ImmutableList
makes a promise - "I am immutable, and I will blow everything up if you try to change me!"and last but not least - proposed method is unnecessary in internal use; just use
directly in your code...
You should check @ChrisPovirk's answer (and link to wiki in that answer) to know that i.e. when
List<Foo> input
containsnull
s, you will get nasty NPE on runtime if you try to make an immutable copy...EDIT answering comment #1:
Collection
contract is less strict thanList
's one; i.e. Collection doesn't guarantee any order of elements ("Some are ordered and others unordered") while List does ("An ordered collection (also known as a sequence)").If an input is a List it suggests that order is important and therefore output should guarantee the same. Imagine that:
It doesn't smell right. If you don't care about order then maybe method signature should be
immutableCopyOfFoo(Collection<Foo> input)
? But it depends on concrete use case.