Saltar al contenido

Cambiar privado static campo final usando la reflexión de Java

Solución:

Asumiendo que no SecurityManager te está impidiendo hacer esto, puedes usar setAccessible para moverse private y restableciendo el modificador para deshacerse de finaly modificar un private static final campo.

He aquí un ejemplo:

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");
      modifiersField.setAccessible(true);
      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"
   

Asumiendo que no SecurityException se lanza, el código anterior se imprime "Everything is true".

Lo que realmente se hace aquí es el siguiente:

  • El primitivo boolean valores true y false en main están encasillados automáticamente con el tipo de referencia Boolean “constantes” Boolean.TRUE y Boolean.FALSE
  • La reflexión se utiliza para cambiar el public static final Boolean.FALSE para referirse a la Boolean referido por Boolean.TRUE
  • Como resultado, posteriormente, siempre que un false está autoencuadrado a Boolean.FALSE, se refiere al mismo Boolean como el referido por Boolean.TRUE
  • Todo lo que fue "false" ahora es "true"

Preguntas relacionadas

  • Usando la reflexión para cambiar static final File.separatorChar para pruebas unitarias
  • ¿Cómo limitar setAccessible solo a usos “legítimos”?
    • Tiene ejemplos de jugar con Integercaché, mutando un String, etc

Advertencias

Se debe tener mucho cuidado siempre que haga algo como esto. Puede que no funcione porque un SecurityManager puede estar presente, pero incluso si no lo hace, dependiendo del patrón de uso, puede o no funcionar.

JLS 17.5.3 Modificación posterior de los campos finales

En algunos casos, como la deserialización, el sistema deberá cambiar la final campos de un objeto después de la construcción. final Los campos se pueden cambiar mediante la reflexión y otros medios dependientes de la implementación. El único patrón en el que esto tiene una semántica razonable es uno en el que se construye un objeto y luego el final Los campos del objeto se actualizan. El objeto no debe hacerse visible para otros hilos, ni el final que se lean los campos, hasta que se actualicen todas las final Los campos del objeto están completos. Congelaciones de un final El campo ocurre tanto al final del constructor en el que el final se establece el campo, e inmediatamente después de cada modificación de un final campo mediante reflexión u otro mecanismo especial.

Incluso entonces, hay una serie de complicaciones. Si un final el campo se inicializa a una constante de tiempo de compilación en la declaración de campo, cambia al final campo puede no ser observado, ya que los usos de ese final los campos se reemplazan en tiempo de compilación con la constante de tiempo de compilación.

Otro problema es que la especificación permite una optimización agresiva de final los campos. Dentro de un hilo, está permitido reordenar las lecturas de un final field con aquellas modificaciones de un campo final que no se producen en el constructor.

Ver también

  • Expresión constante JLS 15.28
    • Es poco probable que esta técnica funcione con un primitivo private static final boolean, porque se puede incorporar como una constante en tiempo de compilación y, por lo tanto, es posible que el valor “nuevo” no sea observable

Apéndice: Sobre la manipulación bit a bit

Esencialmente,

field.getModifiers() & ~Modifier.FINAL

apaga el bit correspondiente a Modifier.FINAL de field.getModifiers(). & es el bit a bit-and, y ~ es el complemento bit a bit.

Ver también

  • Wikipedia / operación bit a bit

Recuerde Expresiones Constantes

¿Sigues sin poder solucionar esto ?, ¿has caído en la depresión como yo lo hice por ello? ¿Tu código se ve así?

public class A 
    private final String myVar = "Some Value";

Al leer los comentarios sobre esta respuesta, especialmente la de @Pshemo, me recordó que las Expresiones constantes se manejan de manera diferente, por lo que será imposible para modificarlo. Por lo tanto, deberá cambiar su código para que se vea así:

public class A 
    private final String myVar;

    private A() 
        myVar = "Some Value";
    

si no eres el dueño de la clase … te siento!

Para obtener más detalles sobre por qué este comportamiento, lea esto.

Si el valor asignado a un static final boolean El campo se conoce en tiempo de compilación, es un constante. Campos de primitivo o
String type pueden ser constantes en tiempo de compilación. Se incluirá una constante en cualquier código que haga referencia al campo. Dado que el campo no se lee realmente en tiempo de ejecución, cambiarlo no tendrá ningún efecto.

La especificación del lenguaje Java dice esto:

Si un campo es una variable constante (§4.12.4), eliminar la palabra clave final o cambiar su valor no romperá la compatibilidad con binarios preexistentes al hacer que no se ejecuten, pero no verán ningún valor nuevo para el uso del campo a menos que se vuelvan a compilar. Este es true incluso si el uso en sí no es una expresión constante en tiempo de compilación (§15.28)

He aquí un ejemplo:

class Flag 
  static final boolean FLAG = true;


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

Si descompila Checker, verá que en lugar de hacer referencia Flag.FLAG, el código simplemente empuja un valor de 1 (true) en la pila (instrucción n. ° 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

Un poco de curiosidad sobre la Especificación del lenguaje Java, capítulo 17, sección 17.5.4 “Campos protegidos contra escritura”:

Normalmente, un campo que es final y static no puede modificarse. Sin embargo, System.in, System.out y System.err son static campos finales que, por razones heredadas, deben poder modificarse mediante los métodos System.setIn, System.setOut y System.setErr. Nos referimos a estos campos como protegidos contra escritura para distinguirlos de los campos finales ordinarios.

Fuente: http://docs.oracle.com/javase/specs/jls/se7/html/jls-17.html#jls-17.5.4

Acuérdate de que te concedemos esclarecer si te fue de ayuda.

¡Haz clic para puntuar esta entrada!
(Votos: 0 Promedio: 0)



Utiliza Nuestro Buscador

Deja una respuesta

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *