99

I came across the following code in a code base I am working on:

public final class ConfigurationService {
    private static final ConfigurationService INSTANCE = new ConfigurationService();
    private List providers;

    private ConfigurationService() {
        providers = new ArrayList();
    }

    public static void addProvider(ConfigurationProvider provider) {
        INSTANCE.providers.add(provider);
    }

    ...

INSTANCE is declared as final. Why can objects be added to INSTANCE? Shouldn't that invalidate the use of final. (It doesn't).

I'm assuming the answer has to do something with pointers and memory but would like to know for sure.

Michu93
  • 5,058
  • 7
  • 47
  • 80
Matt McCormick
  • 13,041
  • 22
  • 75
  • 83
  • This misconception comes up quite often, not necessarily as a question. Often as an answer or comment. – Robin Mar 12 '10 at 19:40
  • 5
    Simple explanation from JLS: **"If a final 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."** [JLS Documentation](http://docs.oracle.com/javase/specs/jls/se7/html/jls-4.html#jls-4.12.4) – realPK Jun 29 '16 at 21:40

7 Answers7

185

final simply makes the object reference unchangeable. The object it points to is not immutable by doing this. INSTANCE can never refer to another object, but the object it refers to may change state.

Gama11
  • 31,714
  • 9
  • 78
  • 100
Sean Owen
  • 66,182
  • 23
  • 141
  • 173
  • 1
    +1, For more details please check http://java.sun.com/docs/books/jls/second_edition/html/typesValues.doc.html, section 4.5.4. – Abel Morelos Mar 12 '10 at 19:22
  • So let's say I deserialize an `ConfigurationService` object and I try to do a INSTANCE = `deserializedConfigurationService` wouldn't be allowed? – diegoaguilar Oct 20 '13 at 18:31
  • You could never assign `INSTANCE` to refer to another object. It doesn't matter where the other object came from. (NB there is one `INSTANCE` per `ClassLoader` that has loaded this class. You could in theory load the class several times in one JVM and each is separate. But this is a different, technical point.) – Sean Owen Oct 20 '13 at 20:04
  • @AkhilGite your edit to my answer made it wrong; it actually reversed the sense of the sentence, which was correct. The reference is immutable. The object remains mutable. It does not "become immutable". – Sean Owen Jun 22 '17 at 13:20
  • @SeanOwen Sry for the edit I did, Your statement is completely right and Thanks. – AkhilGite Jun 22 '17 at 14:10
36

Being final is not the same as being immutable.

final != immutable

The final keyword is used to make sure the reference is not changed ( that is, the reference it has can't be substituted with a new one )

But, if the attribute is self is modifiable it is ok to do what you have just described.

For instance

class SomeHighLevelClass {
    public final MutableObject someFinalObject = new MutableObject();
}

If we instantiate this class, we won't be able to assign other value to the the attribute someFinalObject because it is final.

So this is not possible:

....
SomeHighLevelClass someObject = new SomeHighLevelClass();
MutableObject impostor  = new MutableObject();
someObject.someFinal = impostor; // not allowed because someFinal is .. well final

But if the object it self is mutable like this:

class MutableObject {
     private int n = 0;

     public void incrementNumber() {
         n++;
     }
     public String toString(){
         return ""+n;
     }
}  

Then, the value contained by that mutable object may be changed.

SomeHighLevelClass someObject = new SomeHighLevelClass();

someObject.someFinal.incrementNumber();
someObject.someFinal.incrementNumber();
someObject.someFinal.incrementNumber();

System.out.println( someObject.someFinal ); // prints 3

This has the same effect that your post:

public static void addProvider(ConfigurationProvider provider) {
    INSTANCE.providers.add(provider);
}

Here you are not changing the value of INSTANCE, your are modifying its internal state ( via, providers.add method )

if you want to prevent that the class definition should be changed like this:

public final class ConfigurationService {
    private static final ConfigurationService INSTANCE = new ConfigurationService();
    private List providers;

    private ConfigurationService() {
        providers = new ArrayList();
    }
    // Avoid modifications      
    //public static void addProvider(ConfigurationProvider provider) {
    //    INSTANCE.providers.add(provider);
    //}
    // No mutators allowed anymore :) 
....

But, it might not make much sense :)

By the way, you also have to synchronize access to it basically for the same reason.

Community
  • 1
  • 1
OscarRyz
  • 196,001
  • 113
  • 385
  • 569
28

The key to the misunderstanding is in your question's title. It's not the object which is final, it's the variable. The variable's value can't change, but the data within it can.

Always remember that when you declare a reference type variable, the value of that variable is a reference, not an object.

Jon Skeet
  • 1,421,763
  • 867
  • 9,128
  • 9,194
14

final just means the reference can't be changed. You can't reassign INSTANCE to another reference if it's declared as final. The internal state of the object is still mutable.

final ConfigurationService INSTANCE = new ConfigurationService();
ConfigurationService anotherInstance = new ConfigurationService();
INSTANCE = anotherInstance;

would throw a compilation error

Theo
  • 396
  • 1
  • 5
8

Once a final variable has been assigned, it always contains the same value. If a final 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.

Source

Here's a guide on making an object immutable.

defectivehalt
  • 2,462
  • 3
  • 21
  • 22
5

Final and immutable are not the same thing. Final means the reference cannot be reassigned so you can't say

INSTANCE = ...

Immutable means that the object itself cannot be modified. An example of this is the java.lang.String class. You cannot modify the value of a string.

Gama11
  • 31,714
  • 9
  • 78
  • 100
Chris Dail
  • 25,715
  • 9
  • 65
  • 74
2

Java doesn't have the concept of immutability built into the language. There is no way to mark methods as a mutator. Therefore the language has no way to enforce object immutability.

Gama11
  • 31,714
  • 9
  • 78
  • 100
Steve Kuo
  • 61,876
  • 75
  • 195
  • 257