While an array reference can be declared as final, the elements of the array cannot.
Similarly, while an object reference may be declared as a final field, the object to which it refers may still be mutable. Why so?
While an array reference can be declared as final, the elements of the array cannot.
Similarly, while an object reference may be declared as a final field, the object to which it refers may still be mutable. Why so?
Declaring an object as const
, rather than the pointer has a dramatic impact. In order to ensure the immutability, it is not enough that the code containing the const
declaration must not modify fields through this reference, it is not allowed to invoke any methods on that object which could modify the object. Therefore, every method must be marked as either, potentially modifying the this
instance or guaranteed not to modify it.
Note that the const
attribute is still not a property of the object itself. There may be code having a non-const
reference to the same object being allowed to modify it. Such code may pass the non-const
reference to code accessing the object through a reference with a const
modifier but not vice versa.
So this implies that the const object must not be assigned to another variable which lacks the const
modifier for the referenced object. This formal restriction applies not only to fields but to all local variables, parameters, and return types.
Now think of the beginning of the nineties when there was no Generics in Java. The Collection
classes all simply store Object
s. In that scenario, a support for a const
modifier would imply that such const
objects can never be stored in any collection as there can be no guaranty that the same object is not retrieved from that collection (as plain Object
) and assigned to a non-const
variable.
Starting with Java 8 you can annotate any type use, including the Generic element type of a Collection
or the this
reference an instance method receives. This allows to implement such a const
feature as a @Const
type annotation using an audit tool verifying the const correctness of the code using the annotation. But since it is unlikely that all the existing Java code’s methods suddenly become marked as const
safe or unsafe, it is unlikely that such a feature becomes added to the standard today.
The reason that final
only impacts the actual object instead of the object's properties likely ties back to the fact that Java is pass-by-value.
The definition of a final
variable, per the Java Language Specification, gives us this tidbit, emphasis mine:
Once a
final
variable has been assigned, it always contains the same value. If afinal
variable holds a reference to an object, then the state of the object may be changed by operations on the object, but the variable will always refer to the same object.This applies also to arrays, because arrays are objects; if a final variable holds a reference to an array, then the components of the array may be changed by operations on the array, but the variable will always refer to the same array.
The value of an object reference is the actual location in memory to it, analogous to a pointer. What final
does is state that this pointer cannot* be changed through normal interactions of the program.
If you want immutable references, then use an immutable container. An array is not immutable (as you correctly observe, its values can be changed at will).
*: You can do a lot with reflection and Field#setAccessible()
if you really wanted to, but this utterly defeats the purpose.
If final
would have been a recursive modifier (which is what I think you mean), this would have been horibble. It means that every object pointed by a final
object would be immutable, and so on, and you can end up with the entire memory unmodifyable...
If, for some reason, you want the fields of some instances of your class to be modifyable, while others to have immutable fields, your should probably consider declaring a subclass, or hide private fields with smart setters.