I'm with Arne. The cited section uses the word "immutable"
and "immutability" without defining either. All it says is that
"final" can be an aid to implementing immutable objects -- but it
never says what "immutable" is.
Well, it does. Section 17.5 has several subsections, and it's defined
in section 17.5.1. I feel one really should read all of 17.5 to
understand final fields however.
It doesn't even say that "final"
confers immutability; on the contrary, it says "final fields must
be *used correctly* [emphasis mine] to provide a guarantee of
immutability."
OK, that might be part of the problem. final is necessary but not
sufficient. I'll concede that, no problem. I misspoke if I implied
otherwise.
Toward the end of the section, we're told that String is
"perceived as truly immutable." So now there are two more notions:
"truly immutable" as opposed to merely "immutable," and "perceived
as truly immutable" as opposed to "actually truly immutable, not
dependent on somebody's perception." Where does the JLS define how
these three different kinds of immutability differ? Where does it
*define* even one of them?
That's a bit of an issue. I think they're just using 'perceived' to
mean visible. There's a happens-before relationship between the end of
the ctor and all other threads in the system. So String is perceived as
immutable by all threads because its fields are visible.
I think immutable is defined in section 17.5.1, with their "hb(w, f),
hb(f, a), mc(a, r1), and dereferences(r1, r2)" stuff. That section is
as I say clear as mud, and I'm relying on Brian Goetz in JCIP to
interpret that mess correctly for me.
Thought experiment: Using the JLS' definition of immutability,
support or refute "java.lang.String is mutable, because it computes
its hashCode() lazily and caches it in a non-final field, thereby
changing the String object's state at the first hashCode() call."
But all threads will see the String's internal char[] to have the same
values, because it's safely published through a final field. So all
threads will calculate the same hash code. This isn't really "thread
safe," except that it is because being idempotent counts. Threads can
never seen any value for the internally stored hash code except 0 or the
correctly computed value.
Thought experiment: Using the JLS' definition of immutability,
support or refute "All Java objects are mutable, because all have a
monitor whose state changes every time a synchronized block is
entered or exited. The monitor's state affects the behavior of
all threads attempting to synchronize on the object, so the change
of state is not strictly internal but is visible to all observers."
Refutation: Only objects with proper final field semantics are immutable
under the JLS's definition. Only those objects are thread safe even
without accessing/invoking their monitor; this latter condition was
specified in that short sentence I quoted at the start of this whole
discussion.
"final fields also allow programmers to implement thread-safe immutable
objects without synchronization."
"Without synchronization" is the whole point of the way Java defines
immutability.