Reflection Shenanigans...


class Foo
{
  public final int finalInt = 1;
}

{
  Foo foo = new Foo();
  System.out.println("initial:"+foo.finalInt);	// Prints '1'

  Field f = foo.getClass().getField("finalInt");
  f.setAccessible(true);			// Allowed (!)
  f.setInt(foo, 2);

  System.out.println("after set:"+foo.finalInt); // Prints '1' - ah, so setInt ignored then...
  System.out.println("from field:"+f.getInt());	 // Prints '2' - wtf?
}  

It’s fair to say that whatever I was expecting, that wasn’t it. I was either expecting setAccessible() on a ‘final’ field to throw an exception, or for it to work and for foo.finalInt to actually be changed. In reality it appears that the supposedly final value does get changed internally, but that change isn’t visible to external code.

Anyone know what’s actually going on here? I suspect the VM has decided that since finalInt is final it can keep it cached, but it would be interesting to know quite how it’s doing that.

I don’t like saying this but try google. I’ve come across pretty much the same question a couple of times and there have been ppl explaining it in much better words then I ever could.

The following isn’t the post I was refering to but has hints:
http://stuffthathappens.com/blog/2007/10/13/you-can-change-final-fields/

see a comment:

[quote]For the record, you can change final field value using reflection with
1.1, 1.2, 1.5 and 1.6 VM not with 1.3 and 1.4,
this feature was disable after 1.3 and re-enable in 1.5,
following the recommandation of JSR 133 expert group (memory model).

Rémi
[/quote]
see also:
http://www.javaspecialists.eu/archive/Issue096.html

hmm lovely :slight_smile:

I haven’t read all the links mrlight provided :wink: But I simply would assume the JIT to inline the final field, so if you change it via reflection, the field is changed, but the inlined values are not. Maybe this could be verified with -Xint while executing the testcase (can’t try it for myself atm).

Yep, that’s exactly what is happening.

Inlining of constants is a rather annoying compile-time optimisation that pre-dates the many run-time optimisations that now exist.

I say annoying, because it makes recompilation of individual classes that contain public final values dangerous - as any other class referencing them (that is not part of the same recompilation) will not use the new values.

I would hope that if Java were ever to be rewritten from scratch, this mistake would not be repeated - optimisations that compromise encapsulation should be deferred until run-time, where their implications upon reflection & hotspot can be managed safely.

Can you back this? Actually I am pretty sure that inlining is a runtime optimization and not a compile time one. At least there is no option to control this in javac…

public static final int MY_INTEGER_PI = 3;

You won’t find any getstatic on that field in your bytecode.

It might be worth mentioning that I discovered this with just ‘final’ vars, even ones which are not ‘static final’. Also the same behaviour results if the Field.setInt call is in a separate class, and if ‘finalInt’ is retrieved via a method call. Which would suggest to me that it’s not just simple inlining that’s causing this behaviour.

Ah sorry, missed the fact it was a non-static final. (infact, if it were static final it wouldn’t be possible to set(…) a new value at all!)

I guess the unpredictable behaviour that the Javadoc describes (and you are seeing) could be an artifact of the code caching occuring during jit/hotspot compilation.

I’m surprised nobody has asked the most important question!
Why on earth are you doing this? :slight_smile:

Well I’m not really - I was writing some reflection code and forgot to ignore fields marked ‘final’ and so stumbled upon this behaviour by chance. I’m not so much interested in the behaviour (which although non-intuitive is documented as you point out), but rather why this happens like this. In particular no-one can seem to agree whether it’s the compiler or the VM which is being overly agressive in it’s inlining.

It’s doing the correct thing. Read Chapter 9 of JSR-133 (link above by Mr_Light). By changing “immutables” you’re breaking the program’s claimed meaning, and all bets are off. Not inlining and folding constants would not be enough. You’d have to either treat all field reads as volatile (big performance hit) or have setting a final field invalidate all linked methods which access it (possible with the debugging interface) to trigger recompiles. But this is just the tip of the iceberg. Try this:


import java.lang.reflect.Field;

public class None
{
  public void setup()
  {
    int i;
    Field f;
    
    try {
      f = Integer.class.getDeclaredField("value");
      f.setAccessible(true);

      for(i=0; i<= 256; i++) {
        Integer bi = Integer.valueOf(i);
        f.setInt(bi, 0);
      }
    } catch(Exception e){
      System.out.println(e);
    }
  }
  
  
  public static void main(String[] args)
  {
    None n = new None();
    
    n.setup();
    
    for(int i=0;i<15;i++) {
      System.out.print(i);
      System.out.printf("+10 = %d\n", i+10);
    }
    
    System.exit(0);
  }
}

Now all methods which box integers are broken. For real badness, change the character array of interned Strings.

Ooo, that’s naughty. You get a cookie. ;D

From memory, I think I’ve seen different behaviour in different Sun VMs. Although if you really want reflection shenanigans, you can use sun.misc.Unsafe to actually change the values of final fields…