usage Modifica dei campi finali in Java




using java docs (4)

Questa non è una risposta, ma fa apparire un altro punto di confusione:

Volevo vedere se il problema fosse la valutazione in fase di compilazione o se il riflesso permettesse effettivamente a Java di aggirare la parola chiave final . Ecco un programma di test. Tutto quello che ho aggiunto era un altro gruppo di chiamate getter, quindi ce n'è una prima e dopo ogni chiamata changeField() .

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());
  }
}

Ecco l'output che ottengo (in 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

Perché diavolo fa la chiamata diretta a getWrappedInt () cambia?

Iniziamo con un semplice caso di test:

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());
  }
}

A chiunque importi di indovinare cosa verrà stampato come output (mostrato in basso per non rovinare immediatamente la sorpresa).

Le domande sono:

  1. Perché il numero intero primitivo e incapsulato si comportano in modo diverso?
  2. Perché l'accesso diretto e riflessivo restituisce risultati diversi?
  3. Quello che mi affligge di più - perché String si comporta come int primitivo e non come Integer ?

Risultati (java 1.5):

reflection: primitiveInt = 84
direct: primitiveInt = 42
reflection: wrappedInt = 84
direct: wrappedInt = 84
reflection: stringValue = 84
direct: stringValue = 42

Le costanti di compilazione sono inline (al momento della compilazione di javac). Vedere il JLS, in particolare 15.28 definisce un'espressione costante e 13.4.9 discute la compatibilità binaria o i campi e le costanti finali.

Se si rende il campo non definitivo o si assegna una costante di tempo non compilata, il valore non è in linea. Per esempio:

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


A mio avviso questo è ancora peggio: un collega ha sottolineato la seguente cosa divertente:

@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());
}

In questo modo, è possibile modificare il comportamento dell'intera JVM in esecuzione. (Naturalmente è possibile modificare solo i valori per i valori compresi tra -127 e 127)


C'è una soluzione per questo. se si imposta il valore della finale statica privata archiviato nel blocco statico {}, funzionerà perché non verrà allineato il fileld:

private static final String MY_FIELD;

static {
    MY_FIELD = "SomeText"
}

...

Field field = VisitorId.class.getDeclaredField("MY_FIELD");

field.setAccessible(true);
field.set(field, "fakeText");




final