A Class
isn't truly immutable if any of this children references aren't immutable as well.
If your root Class
has final
references to all its instance variables and all those instance variables are immutable as well as all their children following this same restriction then you can say the root Class
is immutable.
If any of the children references are non-final
or any of the children instances are mutable then the containing Class
is not immutable.
It doesn't matter about the internal immutablity whatever that means is anyone's guess, all that matters is that the public contract to the class immutability is kept. You can't have partial immutability anymore than you can be partially dead. Your class is either immutable or it isn't. And it isn't if any of its referred to classes or their classes, etc. are mutable.
If you have a graph and any of the members of the graph are mutable you can't deterministically say that you have an immutable state. Concurrency guarantees go out the window, .equals()
and .hashCode()
become non-deterministic and simple to test cloning and serializing go out the window as well.
If anything breaks this immutability contract you lose all the benefits of trying to maintain this immutability contract.
Simpler concurrency issues is a main motivational force of immutability.
Having side effect free code helps with predictability and maintainability. Since you don't have to wonder where things are getting mutated in the call tree because you know they can't be mutated.
Performance is another less important factor, in Java the JVM can make some highly optimized decisions about caching and other factors if it knows data can't change state. It does provide important hints to the compiler at compile time and the JIT algorithms at runtime.
Because immutable collections absolutely require sharing to be usable. Otherwise, every single operation drops a whole other list into the heap somewhere. Languages that are entirely immutable, like Haskell, generate astonishing amounts of garbage without aggressive optimizations and sharing. Having collection that's only usable with <50 elements is not worth putting in the standard library.
Further more, immutable collections often have fundamentally different implementations than their mutable counterparts. Consider for example ArrayList
, an efficient immutable ArrayList
wouldn't be an array at all! It should be implemented with a balanced tree with a large branching factor, Clojure uses 32 IIRC. Making mutable collections be "immutable" by just adding a functional update is a performance bug just as much as a memory leak is.
Furthermore, sharing isn't viable in Java. Java provides too many unrestricted hooks to mutability and reference equality to make sharing "just an optimization". It'd probably irk you a bit if you could modify an element in a list, and realize you just modified an element in the other 20 versions of that list you had.
This also rules out huge classes of very vital optimizations for efficient immutability, sharing, stream fusion, you name it, mutability breaks it. (That'd make a good slogan for FP evangelists)
Best Answer
You can't. I'm not familiar with Java 7, but at least in Java 6 you cannot tell the compiler a method is not supposed to mutate its arguments or the
this
instance.Unfortunately
final
in Java doesn't mean the same asconst
in C++.A
final
argument means something else: merely that you cannot reassign it, so that the following is an error:This is a good practice, but it won't prevent you from mutating
arg
by calling one of its methods.A
final
method such asfinal A method(...)
is related to subclassing rules, and not with mutation.