remove - methode.invoke java




Changer le champ final statique privé en utilisant la réflexion Java (6)

Avec la réponse la mieux classée, vous pouvez utiliser une approche un peu plus simple. FieldUtils Apache FieldUtils classe FieldUtils possède déjà une méthode particulière capable de faire les choses. S'il vous plaît, jetez un oeil à la méthode FieldUtils.removeFinalModifier . Vous devez spécifier l'occurrence de champ cible et l'indicateur de forçage d'accessibilité (si vous jouez avec des champs non publics). Plus d'infos vous pouvez trouver here .

J'ai une classe avec un private static final que, malheureusement, j'ai besoin de changer au moment de l'exécution.

En utilisant la réflexion, j'obtiens cette erreur: java.lang.IllegalAccessException: Can not set static final boolean field

Est-il possible de changer la valeur?

Field hack = WarpTransform2D.class.getDeclaredField("USE_HACK");
hack.setAccessible(true);
hack.set(null, true);

En cas de présence d'un gestionnaire de sécurité, on peut utiliser AccessController.doPrivileged

Prenant le même exemple de la réponse acceptée ci-dessus:

import java.lang.reflect.*;

public class EverythingIsTrue {
    static void setFinalStatic(Field field, Object newValue) throws Exception {
        field.setAccessible(true);
        Field modifiersField = Field.class.getDeclaredField("modifiers");

        // wrapping setAccessible 
        AccessController.doPrivileged(new PrivilegedAction() {
            @Override
            public Object run() {
                modifiersField.setAccessible(true);
                return null;
            }
        });

        modifiersField.setInt(field, field.getModifiers() & ~Modifier.FINAL);
        field.set(null, newValue);
    }

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

Dans l'expression lambda, AccessController.doPrivileged , peut être simplifié pour:

AccessController.doPrivileged((PrivilegedAction) () -> {
    modifiersField.setAccessible(true);
    return null;
});

Je l'ai également intégré à la bibliothèque joor

Juste utiliser

      Reflect.on(yourObject).setFinal("finalFieldName", finalFieldValue);

J'ai également corrigé un problème avec override que les solutions précédentes semblent manquer. Cependant, utilisez cela très attentivement, seulement quand il n'y a pas d'autre bonne solution.


Juste vu cette question sur l'une des questions d'entrevue, si possible de changer la variable finale avec réflexion ou en cours d'exécution. Je suis vraiment intéressé, alors ce que je suis devenu:

 /**
 * @author Dmitrijs Lobanovskis
 * @since 03/03/2016.
 */
public class SomeClass {

    private final String str;

    SomeClass(){
        this.str = "This is the string that never changes!";
    }

    public String getStr() {
        return str;
    }

    @Override
    public String toString() {
        return "Class name: " + getClass() + " Value: " + getStr();
    }
}

Une classe simple avec la variable String finale. Donc, dans la classe principale, import java.lang.reflect.Field;

/**
 * @author Dmitrijs Lobanovskis
 * @since 03/03/2016.
 */
public class Main {


    public static void main(String[] args) throws Exception{

        SomeClass someClass = new SomeClass();
        System.out.println(someClass);

        Field field = someClass.getClass().getDeclaredField("str");
        field.setAccessible(true);

        field.set(someClass, "There you are");

        System.out.println(someClass);
    }
}

La sortie sera la suivante:

Class name: class SomeClass Value: This is the string that never changes!
Class name: class SomeClass Value: There you are

Process finished with exit code 0

Selon la documentation https://docs.oracle.com/javase/tutorial/reflect/member/fieldValues.html


Si la valeur affectée à un static final boolean est connue à la compilation, il s'agit d'une constante. Les champs de type primitive ou String peuvent être des constantes de compilation. Une constante sera insérée dans tout code référençant le champ. Puisque le champ n'est pas lu au moment de l'exécution, le modifier n'aura aucun effet.

La spécification du langage Java dit ceci:

Si un champ est une variable constante (§4.12.4), alors la suppression du mot-clé final ou la modification de sa valeur ne rompt pas la compatibilité avec les binaires préexistants en les empêchant de s'exécuter, mais ne voit aucune nouvelle valeur pour l'utilisation du champ à moins qu'ils ne soient recompilés. Ceci est vrai même si l'utilisation elle-même n'est pas une expression de constante de compilation (§15.28)

Voici un exemple:

class Flag {
  static final boolean FLAG = true;
}

class Checker {
  public static void main(String... argv) {
    System.out.println(Flag.FLAG);
  }
}

Si vous décompilez Checker , vous verrez qu'au lieu de référencer Flag.FLAG , le code pousse simplement une valeur de 1 ( true ) sur la pile (instruction # 3).

0:   getstatic       #2; //Field java/lang/System.out:Ljava/io/PrintStream;
3:   iconst_1
4:   invokevirtual   #3; //Method java/io/PrintStream.println:(Z)V
7:   return

Tout le point d'un champ final est qu'il ne peut pas être réaffecté une fois qu'il a été défini. La JVM utilise cette garantie pour maintenir la cohérence à divers endroits (par exemple, des classes internes référençant des variables externes). Donc non. Etre capable de le faire casserait la JVM!

La solution n'est pas de le déclarer final en premier lieu.





final