19

It might sound a bit strange, but is it possible to add the final modifier at runtime?

I have a variable which is marked as public static int/short. At certain point I want to prevent changing its value, and I want to keep its accessibility as a standard static value (ClassName.field).

public class Main {

    private static int a = 0;

    public static void addFinalMod(Field f) {

        Field modifiersField = null;
        try {
            modifiersField = Field.class.getDeclaredField("modifiers");
        }
        catch (NoSuchFieldException e) {
            e.printStackTrace();
        }
        modifiersField.setAccessible(true);
        try {
            modifiersField.setInt(f, f.getModifiers() & Modifier.FINAL);
        }
        catch (IllegalAccessException e) {
            e.printStackTrace();
        }
    }

    public static void main(String[] args) {

        System.out.println(a);
        try {
            Field f = Main.class.getDeclaredField("a");
            addFinalMod(f);
        }
        catch (NoSuchFieldException e) {
            e.printStackTrace();
        }
        a = 10; //I was expecting error/exception here
        System.out.println(a);
    }

Output:

0
10
Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
user2749903
  • 1,275
  • 1
  • 10
  • 20
  • 1
    This is quite interesting question, actually. For now, to me it seems like `final` rules are checked only during compile-time (this includes both initialization requirements and JMM guarantees). – Victor Sorokin Jan 16 '15 at 19:26
  • 3
    I think you mean `f.getModifiers() | Modifier.FINAL` instead `f.getModifiers() & Modifier.FINAL` to let other modifiers stay. Also if you will use `f.set(null, 5);` you will get expected exception. – Pshemo Jan 16 '15 at 19:29

2 Answers2

34

At certain point I want to prevent changing its value

Do that in application logic. Make the variable only accessible through methods. Keep a flag tracking any change to the variable. After you've applied the change or reached the certain point, raise the flag, and throw an exception for any further attempts at changing the variable.

final is a language keyword/feature for writing your source code and preventing re-assignment of a variable in source code. At runtime, it does (almost) nothing.

Sotirios Delimanolis
  • 274,122
  • 60
  • 696
  • 724
  • 1
    This is what I would do too, altought I'd like to see a real solution but I doubt it's possible. – Jean-François Savard Jan 16 '15 at 19:28
  • 1
    I know, this sollution will work and is very easy to implement. but i'd prefer to not using any methods. – user2749903 Jan 16 '15 at 19:31
  • 7
    @user2749903 Then you're out of luck. Can't be done with straight variable access. What you basically want is a function that looks like a variable, which Java doesn't have (other languages do, but Java doesn't). – yshavit Jan 16 '15 at 19:33
  • @yshavit _"Upvote and a brownie point for "(almost)""_ Could you elaborate a little ? AFAIK, JVM spec does not require _any_ special processing at run-time for _fields_ having the `ACC_FINAL` access flag set. But some JVM implementation _might_ probably use that flag for optimization purpose though. Is that what you have in mind ? – Sylvain Leroux Jan 16 '15 at 23:42
  • 2
    @SylvainLeroux Thread safety, JLS 17.5. Basically, as long as you don't leak the `this` reference from a constructor, any `final` field is guaranteed to be seen from any thread in (at least) the state that it was at construction time. For instance, `ArrayList` isn't usually thread safe, but if you have a `final ArrayList` that you instantiate and fill in your constructor, _and you never modify it after the constructor_, then any thread that can see your object is guaranteed to see the `ArrayList` fully constructed (including seeing any objects that it holds fully constructed, etc). – yshavit Jan 16 '15 at 23:51
  • 3
    And the neat/special thing is that that's true even if the object itself (that has the `final` field) is published in a non-thread-safe way. A typical example (and I believe the motivating one, historically) is `String`. It has a `final char[]`, but before Java 1.5 (when these new `final` semantics were added), you could still see partially constructed values if one thread created a `String` and published it non-safely to another (e.g., by setting it in a non-volatile `static` field). With these "new" semantics, if you can access that `String`, then you'll see exactly the `char[]` you should. – yshavit Jan 16 '15 at 23:54
2

Use an immutable object:

public class Thing {
    private int value;
    public Thing(int value) { this.value = value; }
    public int getValue() { return this.value; }
}

Then just replace it when it needs modification:

Thing x = Thing(1);
// do some stuff
x = Thing(2);
// more stuff

If you're thinking, "But then they can still modify the value by replacing the object!" well, of course. The only way to prevent that is to lock down some global state (which is actually impossible if it's not a constant), but you're not using global state, right? Global state is a Bad ThingTM.

Global state has a very simple problem: it makes your code much less reusable and more error prone. Error prone is especially true if you're modifying that global state at runtime. "Aw, crap, things are happening in the wrong order." You have no idea how easy that is. Trying to track order of events is the reason mutlithreading is so hard. In terms of reusability, it means that you can't have two independent instances of the thing, which in turn implies you can't really have any two instances of anything that depends on it (at least when it turns out you needed to vary this thing after all).

Even if this isn't a global, then this is some weird state that everyone who has to modify your code (including yourself) is going to have to be aware of and thinking about as they make changes. Does that sound like it's going to make changing things easier or harder? (Hint: the latter.) Don't do this to yourself or your coworkers. Make it simpler. Take a lesson from the Zen of Python: "Simple is better than complex."

So bottom line: if you need an immutable object, use an immutable object. If you need an immutable global that's not a constant, figure out a better way to organize your code.

jpmc26
  • 28,463
  • 14
  • 94
  • 146