I can see that Collections.unmodifiableSet
returns an unmodifiable view of the given set but I don't understand why we can't just use the final
modifier to accomplish this.
In my understanding, final
declares a constant: something that cannot be modified. So, if a set is declared as a constant then it cannot be modified: nothing can be removed from the set and nothing can be added.
Why do we need Collections.unmodifiableSet
?
final
declares an object reference that can't be modified, e.g.
private final Foo something = new Foo();
creates a new Foo
and places the reference in something
. Thereafter, it's not possible to alter something
to point to a different instance of Foo
.
This does not prevent modification of the internal state of the object. I can still call whatever methods on Foo
there are accessible to the relevant scope. If one or more of those methods modifies the internal state of that object, then final
won't prevent that.
As such, the following:
private final Set<String> fixed = new HashSet<String>();
does not create a Set
that can't be added to or otherwise altered; it just means that fixed
will only ever reference that instance.
By contrast, doing:
private Set<String> fixed = Collections.unmodifiableSet( new HashSet<String>() );
creates an instance of a Set
which will throw UnsupportedOperationException
if one attempts to call fixed.add()
or fixed.remove()
, for example - the object itself will protect its internal state and prevent it from being modified.
For completeness sake:
private final Set<String> fixed = Collections.unmodifiableSet( new HashSet<String>() );
creates an instance of a Set
which won't allow its internal state to be changed, and also means that fixed
will only ever point to an instance of that set.
The reason that final
can be used to create constants of primitives is based on the fact that the value can't be changed. Remember that fixed
above was just a reference - a variable containing an address that can't be changed. Well, for primitives, e.g.
private final int ANSWER = 42;
the value of ANSWER
is that 42. Since ANSWER
can't be changed, it will only ever have the value 42.
An example that blurs all the lines would be this:
private final String QUESTION = "The ultimate question";
Per the rules above, QUESTION
contains the address of an instance of String
which represents "The ultimate question", and that address can't be changed. The thing to remember here is that String
itself is immutable - you can't do anything to an instance of String
which changes it, and any operations which would otherwise do so (such as replace
, substring
, etc.) return references to entirely different instances of String
.