java dependencies bytecode javac

private static final java



¿Es posible inhabilitar la inclusión de variables estáticas finales de javac en javac? (9)

Creo que este es un error serio . Java no es C / C ++. Existe un principio (o no) "Compilar una vez, ejecutar en todas partes".

En este caso, cuando se cambia la Clase A. Cualquier clase que haga referencia a A.CONST_VALUE se debe volver a compilar y difícilmente saben si se cambia la Clase A.

El compilador estático de Java (javac) enumera algunas variables finales estáticas y trae los valores directamente al conjunto constante. Considera el siguiente ejemplo. La clase A define algunas constantes (variables finales estáticas públicas):

public class A { public static final int INT_VALUE = 1000; public static final String STRING_VALUE = "foo"; }

La clase B usa estas constantes:

public class B { public static void main(String[] args) { int i = A.INT_VALUE; System.out.println(i); String s = A.STRING_VALUE; System.out.println(s); } }

Cuando compila la clase B, javac obtiene los valores de estas constantes de la clase A y los inserta en B.class. Como resultado, la dependencia B de la clase A en el momento de la compilación se borra del bytecode. Este es un comportamiento bastante peculiar porque está horneando los valores de estas constantes en el momento de la compilación . Y uno pensaría que esta es una de las cosas más fáciles que el compilador JIT puede hacer en tiempo de ejecución.

¿Hay alguna manera o alguna opción de compilador oculto que te permita desactivar este comportamiento de javac? En segundo plano, estamos estudiando la posibilidad de realizar un análisis de bytecode con fines de dependencia, y es uno de los pocos casos en que el análisis de bytecode no detecta las dependencias en tiempo de compilación. ¡Gracias!

Editar : este es un problema molesto porque normalmente no controlamos todas las fuentes (por ejemplo, bibliotecas de terceros que definen constantes). Estamos interesados ​​en detectar estas dependencias desde la perspectiva del uso de las constantes. Como la referencia se borra del código que usa las constantes, no hay una manera fácil de detectarlas, salvo el análisis del código fuente.


El ítem 93 de Puzzlers de Java (Joshua Bloch) dice que puedes evitar esto impidiendo que el valor final sea considerado una constante. Por ejemplo:

public class A { public static final int INT_VALUE = Integer.valueOf(1000).intValue(); public static final String STRING_VALUE = "foo".toString(); }

Por supuesto, nada de esto es relevante si no tiene acceso al código que define las constantes.


Para dejar de alinear, necesita convertir los valores en constantes de tiempo que no sean de compilación (el término JLS). Puede hacerlo sin el uso de funciones y creando un mínimo de bytecode utilizando un null en la expresión del inicializador.

public static final int INT_VALUE = null!=null?0: 1000;

Aunque es muy literal en su generación de código, javac debería optimizar esto para que sea una inserción de un entero inmediato seguido de una tienda en el campo estático en el inicializador estático.


Recientemente me encontré con un issue similar, y, como se dijo anteriormente, dicha línea puede resolverse utilizando expresiones que no son de compilación, por ejemplo:

public final class A { public static final int INT_VALUE = constOf(1000); public static final String STRING_VALUE = constOf("foo"); }

donde la familia del método constOf es simplemente:

// @formatter:off public static boolean constOf(final boolean value) { return value; } public static byte constOf(final byte value) { return value; } public static short constOf(final short value) { return value; } public static int constOf(final int value) { return value; } public static long constOf(final long value) { return value; } public static float constOf(final float value) { return value; } public static double constOf(final double value) { return value; } public static char constOf(final char value) { return value; } public static <T> T constOf(final T value) { return value; } // @formatter:on

Esto es un poco más corto que otras sugerencias como Integer.valueOf(1000).intValue() o null!=null?0: 1000 Integer.valueOf(1000).intValue() null!=null?0: 1000


Reescribe la clase A como:

public class A { public static final int INT_VALUE; public static final String STRING_VALUE; static { INT_VALUE = 1000; STRING_VALUE = "foo"; } }


Siento java firmemente se basa en la compilación dinámica y no hace ninguna lógica de compilación de lujo como C ++.

puede probar algunas opciones con los compiladores JIT que optimiza el tiempo de ejecución, que puede tener algunas opciones para deshabilitar / habilitar esto.

en javac predeterminado no puede obtener esa opción. tienes que usar 1. algún tipo de gráfico de dependencia como extends o implements 2. utilizando un enlace basado en método.

-s


Yo no lo creo. La solución más simple sería exponer estos como propiedades en lugar de campos:

public class A { private static final int INT_VALUE = 1000; private static final String STRING_VALUE = "foo"; public static int getIntValue() { return INT_VALUE; } public static String getStringValue() { return STRING_VALUE; } }

No olvide que, en ciertos casos, la incorporación es esencial para el uso del valor; por ejemplo, si tuviera que usar INT_VALUE como un caso en un bloque de conmutadores, debe especificarse como un valor constante.


JLS 13.4.9 trata este problema. Su recomendación es básicamente evitar las constantes de tiempo de compilación si es probable que el valor cambie.

(Una razón para requerir la creación de constantes es que las sentencias switch requieren constantes en cada caso, y no hay dos valores constantes iguales. El compilador comprueba los valores constantes duplicados en una instrucción switch en el momento de la compilación, el formato de archivo de clase no hacer un enlace simbólico de los valores de casos).

La mejor manera de evitar problemas con las "constantes inconstantes" en el código ampliamente distribuido es declarar como constantes de tiempo de compilación solo valores que realmente es poco probable que cambien alguna vez. Aparte de las verdaderas constantes matemáticas, recomendamos que el código fuente haga un uso muy moderado de las variables de clase declaradas estáticas y finales. Si se requiere la naturaleza de solo lectura del final, una mejor opción es declarar una variable estática privada y un método de acceso adecuado para obtener su valor. Por lo tanto, recomendamos:

private static int N; public static int getN() { return N; }

más bien que:

public static final int N = ...;

No hay problema con:

public static int N = ...;

si N no necesita ser de solo lectura.


jmake es un proyecto de código abierto que afirma hacer todo el trabajo de hacer un seguimiento de las dependencias entre los archivos Java y compilar de forma incremental el conjunto mínimo de archivos necesarios. Afirma que maneja correctamente los cambios a las constantes finales estáticas, aunque a veces requiere la recompilación del proyecto completo. Incluso maneja los cambios en una granularidad más fina que classfiles; si (por ejemplo) la firma de un método Cm () cambia, entonces solo recompila las clases que realmente dependen de m () en lugar de todas las clases que usan C.

DESCARGO DE RESPONSABILIDAD: No tengo experiencia en usar jmake.