45

Could anyone please tell me what is the meaning of the following line in context of Java:

final variable can still be manipulated unless it's immutable

As far as I know, by declaring any variable as final, you can't change it again, then what they mean with the word immutable in above line?

Dusk
  • 2,191
  • 6
  • 38
  • 57

11 Answers11

98

It means that if your final variable is a reference type (i.e. not a primitive like int), then it's only the reference that cannot be changed. It cannot be made to refer to a different object, but the fields of the object it refers to can still be changed, if the class allows it. For example:

final StringBuffer s = new StringBuffer();

The content of the StringBuffer can still be changed arbitrarily:

s.append("something");

But you cannot say:

s = null;

or

s = anotherBuffer;

On the other hand:

final String s = "";

Strings are immutable - there simply isn't any method that would enable you to change a String (unless you use Reflection - and go to hell).

Michael Borgwardt
  • 342,105
  • 78
  • 482
  • 720
  • @pramodc84: It's the same for all variables. Most collections are mutable though. – Michael Borgwardt Oct 04 '11 at 06:41
  • So how can I change it with reflection? =) – Daniel Magnusson Dec 07 '12 at 14:30
  • 1
    @Daniel Magnusson: use http://docs.oracle.com/javase/7/docs/api/java/lang/reflect/AccessibleObject.html#setAccessible(boolean) to make the private `char[]` inside `String` accessible. – Michael Borgwardt Dec 07 '12 at 14:33
  • 18
    +1 *unless you use Reflection - and go to hell* - just love a good humor coming from a bad experience :))) – luigi7up Feb 06 '13 at 10:01
  • Is the following code valid ? If yes, How ? It works for me. final Task newTask[] = new Task[1]; newTask[0] = someMethod(); if(!newTask[0].isCompleted()){ newTask[0] = null; } – Viraj Sep 30 '15 at 16:42
  • 1
    @Viraj: In Java, only variables can be final, not objects. So in your code, final only applies to the reference newTask. But the array object which it refers to can sill have its elements changed. – Michael Borgwardt Oct 01 '15 at 07:15
16

If you have a final reference to a Java object you can still manipulate it but cannot change its reference. For instance this code is perfectly legal:

import javax.swing.JLabel;

class Test1 {
    private final static JLabel l = new JLabel("Old text");
    public static void main(String[] args) {
        System.err.println(l.getText());
        l.setText("New Text");
        System.err.println(l.getText());
    }
}

But you can't say:

l = new JLabel("Newest Text");

After the first assignment to l. Note that you can do this though:

import javax.swing.JLabel;

class Test1 {
    public static void main(String[] args) {
        final JLabel l;
        String s = getArbitaryString(); // Assume this method returns a string
        l = new JLabel(s);
        System.err.println(l.getText());
    }
}

This can be done because when l is declared it is not assigned to anything not even null. So you are allowed to assign something to it one time only.

Same thing goes for primitives. You can assign a value to it like this:

class Test1 {
    public static void main(String[] args) {
        final int i;
        i = 2;
    }
}

But now you cannot manipulate it further since the only thing you can do to primitive types is to assign values to them.

Jim Ferrans
  • 30,582
  • 12
  • 56
  • 83
Savvas Dalkitsis
  • 11,476
  • 16
  • 65
  • 104
4

You cannot change what object or value a final variable refers to. You can only assign a final variable once.

This has no effect on whether you can change the state of the object. The object itself can still be manipulated unless it is coded in such a way that this manipulation is forbidden. An immutable object is an object whose state cannot change.

Fred
  • 119
  • 4
3

As others have said, it means that you can manipulate the object the variable points at, but you cannot change the reference (i.e. assign another object to the variable).

Objects that are mutable by design, such as a List can be changed (you can add elements to them) whereas if you have an immutable object such as a String or Integer you won't be able to change it (all the operations the class String supports return a new instance, and don't modify the actual object).

pgb
  • 24,813
  • 12
  • 83
  • 113
0

You can manipulate mutable final variables for e.g. of type StringBuffer but you cannot manipulate final variables of immutable types.

In case of mutable variables, new object is not created every time it's value is changed. But in case of of immutable types, whenever you change value, new object is created, so when you make it final, you cannot modify it.

Community
  • 1
  • 1
deepakraut
  • 893
  • 1
  • 10
  • 17
0

You can call any method on it even if the method can change the state of the object the reference is pointing to. E.g

final MyClass myClass = new MyClass();
myClass.setVar(something);

This is fine because myClass itself is not changing, i.e you are not doing myClass = myClass1;.

fastcodejava
  • 39,895
  • 28
  • 133
  • 186
0

The one that always kills me?

If you want final variables to actually be as safe as you thought they were, you need a lot of extra code to return a copy of a String[].

Dean J
  • 39,360
  • 16
  • 67
  • 93
0

Yes the final variable can be modified.

    final StringBuffer s = new StringBuffer();
    // won't work
    s = new StringBuffer();
    //this works
    s.append("hai");

You can't change the reference but the fields of the the object can be modified. for more details

Andrew Alderson
  • 893
  • 12
  • 18
0

The following is code I created several years back to turn final off and then back on so you can modify the reference/value, it will only work on variables, but it does work.

You can also do something similar with method handles, however unless you are writing some form of auto object parser/generator, I would avoid doing either like the plague.

public static void setValueOnField(Object instance, Field field, Object value)
        throws NoSuchFieldException, IOException, IllegalArgumentException,
        IllegalAccessException {
    try (Accessor<Field> access = open(field)) {
        field.set(instance, value);
    }
}


  public static class Accessor<T extends AccessibleObject & Member>
            implements Closeable {
        private final boolean isAccessible;
        private final boolean isFinal;
        private final int modifiers;
        private final T accessibleObject;

        private Accessor(T accessibleObject) throws IOException {
            super();
            if (accessibleObject == null) {
                throw new IOException(
                        "Error preparing field for accesibility: Field is null");
            }
            try {
                this.accessibleObject = accessibleObject;
                this.modifiers = accessibleObject.getModifiers();
                this.isAccessible = accessibleObject.isAccessible();
                this.isFinal = Modifier.isFinal(modifiers);
                if (!this.isAccessible) {
                    accessibleObject.setAccessible(true);
                }
                if (this.isFinal) {
                    getModifiersField(accessibleObject).setInt(
                            accessibleObject, modifiers & ~Modifier.FINAL);
                }
            } catch (Exception e) {
                throw new IOException("Error preparing field for accesibility",
                        e);
            }

        }

        @Override
        public void close() throws IOException {

            if (!this.isAccessible) {
                accessibleObject.setAccessible(false);
            }
            if (this.isFinal) {
                try {
                    getModifiersField(accessibleObject).setInt(
                            accessibleObject, modifiers);
                } catch (Exception e) {
                    throw new IOException("Error setting field to final", e);
                }
            }
        }

        public T getAccessibleObject() {
            return accessibleObject;
        }

        private static Field getModifiersField(AccessibleObject toFetch) {
            Field field;
            try {
                field = toFetch.getClass().getField("modifiers");
                field.setAccessible(true);
                return field;
            } catch (Exception e) {
                throw new RuntimeException(
                        "Error occured getting modifiers field", e);
            }
        }
    }
0

There two things Final variable and final reference variable.

If we are using final keyword with primitive data type we can't change anything. But if we are using final keywprd with non - primitive data, we can change it's properties like:

  1. If you are using a final keyword with primitive types of the variable (int, float, char, etc) then you can’t change the value of a final variable once it is initialized. So, we should initialize it.

  2. If you are using a final keyword with non-primitive variables (By means of non-primitive variables are always references to objects in Java), the member of the object can be changed. It means we can change the properties of the object, but we can’t change to refer to any other object.

https://javagoal.com/final-keyword-in-java/

-2

You can still change a 'final' variable using Reflection.

Javamann
  • 2,882
  • 2
  • 25
  • 22
  • 1
    You can but that's not what he meant. – Savvas Dalkitsis Aug 08 '09 at 21:20
  • Actually, you can't. You would get a java.lang.IllegalAccessException. – Aviad Ben Dov Aug 08 '09 at 21:45
  • 2
    No you can't. You get `java.lang.IllegalAccessException: Can not set final [type] field [fieldname] to [type of new value]`. – polygenelubricants Feb 27 '10 at 14:10
  • It's documented here too: http://java.sun.com/docs/books/tutorial/reflect/member/fieldTrouble.html -- "IllegalAccessException when Modifying Final Fields" – polygenelubricants Feb 27 '10 at 14:11
  • 2
    @polygenelubricants - http://java.sun.com/javase/6/docs/api/java/lang/reflect/Field.html#set(java.lang.Object,%20java.lang.Object) says this: "If the underlying field is final, the method throws an IllegalAccessException *unless setAccessible(true) has succeeded for this field and this field is non-static*." – Stephen C Feb 27 '10 at 14:27
  • @Stephen C: thanks for pointing this out! I'm still learning and this is new to me. Very surprising. I will do my research now. – polygenelubricants Feb 27 '10 at 15:03