2012-12-31 19 views
5

Niedawno natknąłem się na Change private static final field using Java reflection i przetestowałem polygenelubricants 'EverythingIsTrue klasy, działa dobrze, System.out.format("Everything is %s", false); drukuje Everything is true rzeczywiście. Ale kiedy zmiany kodu jakoNie można zmienić statycznego pola końcowego przy użyciu odbicia Java?

public class EverythingIsTrue { 

    public static final boolean FALSE = false; 

    static void setFinalStatic(Field field, Object newValue) throws Exception { 
     field.setAccessible(true); 
     Field modifiersField = Field.class.getDeclaredField("modifiers"); 
     modifiersField.setAccessible(true); 
     modifiersField.setInt(field, field.getModifiers() & ~Modifier.FINAL); 
     field.set(null, newValue); 
    } 

    public static void main(String[] args) throws Exception { 
     setFinalStatic(EverythingIsTrue.class.getField("FALSE"), true); 
     System.out.format("Everything is %s", FALSE); 
    } 
} 

drukuje

Everything is false 

Czy ktoś wie dlaczego? Czy setFinalStatic rzeczywiście działa, czy nie?

Odpowiedz

13

Podczas uzyskiwania dostępu do pierwotnych statycznych pól końcowych, kompilator Java przyjmuje, że wartość jest stałą i wstawia wartość zamiast generowania kodu, który uzyskuje dostęp do pola. Oznacza to, że kompilator zastąpi odniesienie do pola FALSE o wartości false. Jeśli użyjesz odbicia, aby uzyskać dostęp do pola, zobaczysz, że wartość pola rzeczywiście się zmieniła.

Nie będzie działać dla pól niepochodzących z pierwotnych, ponieważ wartość odwołania do obiektu nie może być wstawiona podczas kompilacji.

+1

która jest dokładnie taka sama odpowiedź jak ja - tylko z większą ilością słów! – dty

+0

@dty wygląda na to, że są nieskończone więcej słów nawet;) –

+0

Usunąłem moją odpowiedź, ponieważ Twoja została zaakceptowana, a moja nie dodawała żadnej wartości - tylko zwięzłość! ;-) – dty

17

Możesz uniknąć wprowadzania kompilacji, nadając wartość wynikowi wywołania metody, nawet fałszywej.

public class Main { 
    // value is not known at compile time, so not inlined 
    public static final boolean FLAG = Boolean.parseBoolean("false"); 

    static void setFinalStatic(Class clazz, String fieldName, Object newValue) throws NoSuchFieldException, IllegalAccessException { 
     Field field = clazz.getDeclaredField(fieldName); 
     field.setAccessible(true); 
     Field modifiers = field.getClass().getDeclaredField("modifiers"); 
     modifiers.setAccessible(true); 
     modifiers.setInt(field, field.getModifiers() & ~Modifier.FINAL); 
     field.set(null, newValue); 
    } 

    public static void main(String... args) throws Exception { 
     System.out.printf("Everything is %s%n", FLAG); 
     setFinalStatic(Main.class, "FLAG", true); 
     System.out.printf("Everything is %s%n", FLAG); 
    } 
} 

drukuje

Everything is false 
Everything is true 
+0

To jest niesamowite! Używając metody ogólnej, możesz stworzyć metodę pozorną dla nie-prymitywów 'statyczny T placeHolder() {return null; } ' – flakes

+0

@ mc-emperor, poprzednia wersja była bardziej czytelna. – dit

+0

@dit Jest to wyjście konsoli, więc nie powinno być sformatowane tak, jakby był kodem źródłowym. –

Powiązane problemy