Skip to main content

Modifying final fields in Java



Let's start with a simple test case:







import java.lang.reflect.Field;



public class Test {

private final int primitiveInt = 42;

private final Integer wrappedInt = 42;

private final String stringValue = "42";



public int getPrimitiveInt() { return this.primitiveInt; }

public int getWrappedInt() { return this.wrappedInt; }

public String getStringValue() { return this.stringValue; }



public void changeField(String name, Object value) throws IllegalAccessException, NoSuchFieldException {

Field field = Test.class.getDeclaredField(name);

field.setAccessible(true);

field.set(this, value);

System.out.println("reflection: " + name + " = " + field.get(this));

}



public static void main(String[] args) throws IllegalAccessException, NoSuchFieldException {

Test test = new Test();



test.changeField("primitiveInt", 84);

System.out.println("direct: primitiveInt = " + test.getPrimitiveInt());



test.changeField("wrappedInt", 84);

System.out.println("direct: wrappedInt = " + test.getWrappedInt());



test.changeField("stringValue", "84");

System.out.println("direct: stringValue = " + test.getStringValue());

}

}







Anybody care to guess what will be printed as output (shown at the bottom as to not spoil the surprise immediately).





The questions are:





  1. Why do primitive and wrapped integer behave differently?



  2. Why does reflective vs direct access return different results?



  3. The one that plagues me most - why does String behave like primitive int and not like Integer ?







Results (java 1.5):







reflection: primitiveInt = 84

direct: primitiveInt = 42

reflection: wrappedInt = 84

direct: wrappedInt = 84

reflection: stringValue = 84

direct: stringValue = 42




Comments

  1. Compile-time constants are inlined (at javac compile-time). See the JLS, in particular 15.28 defines a constant expression and 13.4.9 discusses binary compatibility or final fields and constants.

    If you make the field non-final or assign a non-compile time constant, the value is not inlined. For instance:

    private final String stringValue = null!=null?"": "42";

    ReplyDelete
  2. Reflection's set(..) method works with FieldAccessors.

    For int it gets an UnsafeQualifiedIntegerFieldAccessorImpl, whose superclass defines the readOnly property to be true only if the field is both static and final

    So to first answer the unasked question - here's why the final is changed without exception.

    All subclasses of UnsafeQualifiedFieldAccessor use the sun.misc.Unsafe class to get the values. The methods there are all native, but their names are getVolatileInt(..) and getInt(..) (getVolatileObject(..) and getObject(..) respectively). The aforementioned accessors use the "volatile" version. Here's what happens if we add the non-volatile version:

    System.out.println("reflection: non-volatile primitiveInt = "
    unsafe.getInt(test, (long) unsafe.fieldOffset(getField("primitiveInt"))));


    (where unsafe is instantiated by reflection - it is not allowed otherwise)
    (and I call getObject for Integer and String)

    That gives some interesting results:

    reflection: primitiveInt = 84
    direct: primitiveInt = 42
    reflection: non-volatile primitiveInt = 84
    reflection: wrappedInt = 84
    direct: wrappedInt = 84
    reflection: non-volatile wrappedInt = 84
    reflection: stringValue = 84
    direct: stringValue = 42
    reflection: non-volatile stringValue = 84


    At this point I recall an article at javaspecialists.eu discussing an related matter. It quotes JSR-133:


    If a final field is initialized to a compile-time constant in the field declaration, changes to the final field may not be observed, since uses of that final field are replaced at compile time with the compile-time constant.


    Chapter 9 discusses the details observed in this question.

    And it turns out this behaviour is not that unexpected, since modifications of final fields are supposed to happen only right after initialization of the object.

    ReplyDelete
  3. This is not an answer, but it brings up another point of confusion:

    I wanted to see if the issue was compile-time evaluation or whether the reflection was actually allowing Java to get around the final keyword. Here's a test program. All I added was another set of getter calls, so there's one before and after each changeField() call.

    package com.example.gotchas;

    import java.lang.reflect.Field;

    public class MostlyFinal {
    private final int primitiveInt = 42;
    private final Integer wrappedInt = 42;
    private final String stringValue = "42";

    public int getPrimitiveInt() { return this.primitiveInt; }
    public int getWrappedInt() { return this.wrappedInt; }
    public String getStringValue() { return this.stringValue; }

    public void changeField(String name, Object value) throws IllegalAccessException, NoSuchFieldException {
    Field field = MostlyFinal.class.getDeclaredField(name);
    field.setAccessible(true);
    field.set(this, value);
    System.out.println("reflection: " + name + " = " + field.get(this));
    }

    public static void main(String[] args) throws IllegalAccessException, NoSuchFieldException {
    MostlyFinal test = new MostlyFinal();

    System.out.println("direct: primitiveInt = " + test.getPrimitiveInt());
    test.changeField("primitiveInt", 84);
    System.out.println("direct: primitiveInt = " + test.getPrimitiveInt());

    System.out.println();

    System.out.println("direct: wrappedInt = " + test.getWrappedInt());
    test.changeField("wrappedInt", 84);
    System.out.println("direct: wrappedInt = " + test.getWrappedInt());

    System.out.println();

    System.out.println("direct: stringValue = " + test.getStringValue());
    test.changeField("stringValue", "84");
    System.out.println("direct: stringValue = " + test.getStringValue());
    }
    }


    Here's the output I get (under Eclipse, Java 1.6)

    direct: primitiveInt = 42
    reflection: primitiveInt = 84
    direct: primitiveInt = 42

    direct: wrappedInt = 42
    reflection: wrappedInt = 84
    direct: wrappedInt = 84

    direct: stringValue = 42
    reflection: stringValue = 84
    direct: stringValue = 42


    Why the heck does the direct call to getWrappedInt() change ?

    ReplyDelete
  4. In my opinion this is even worse: A colleague pointed to the following funny thing:

    @Test public void testInteger() throws SecurityException, NoSuchFieldException, IllegalArgumentException, IllegalAccessException {    
    Field value = Integer.class.getDeclaredField("value");    
    value.setAccessible(true);    
    Integer manipulatedInt = Integer.valueOf(7);    
    value.setInt(manipulatedInt, 666);    
    Integer testInt = Integer.valueOf(7);    
    System.out.println(testInt.toString());
    }


    By doing this, you can change the behaviour of the whole JVM you are running in.
    (of course you can change only the values for the values between -127 and 127)

    ReplyDelete

Post a Comment

Popular posts from this blog

[韓日関係] 首相含む大幅な内閣改造の可能性…早ければ来月10日ごろ=韓国

div not scrolling properly with slimScroll plugin

I am using the slimScroll plugin for jQuery by Piotr Rochala Which is a great plugin for nice scrollbars on most browsers but I am stuck because I am using it for a chat box and whenever the user appends new text to the boxit does scroll using the .scrollTop() method however the plugin's scrollbar doesnt scroll with it and when the user wants to look though the chat history it will start scrolling from near the top. I have made a quick demo of my situation http://jsfiddle.net/DY9CT/2/ Does anyone know how to solve this problem?

Why does this javascript based printing cause Safari to refresh the page?

The page I am working on has a javascript function executed to print parts of the page. For some reason, printing in Safari, causes the window to somehow update. I say somehow, because it does not really refresh as in reload the page, but rather it starts the "rendering" of the page from start, i.e. scroll to top, flash animations start from 0, and so forth. The effect is reproduced by this fiddle: http://jsfiddle.net/fYmnB/ Clicking the print button and finishing or cancelling a print in Safari causes the screen to "go white" for a sec, which in my real website manifests itself as something "like" a reload. While running print button with, let's say, Firefox, just opens and closes the print dialogue without affecting the fiddle page in any way. Is there something with my way of calling the browsers print method that causes this, or how can it be explained - and preferably, avoided? P.S.: On my real site the same occurs with Chrome. In the ex