2009-09-10 15 views
18

Considere este caso:es Java garantizado para inline constantes de cadena si se pueden determinar en tiempo de compilación

public Class1 { 
    public static final String ONE = "ABC"; 
    public static final String TWO = "DEF"; 
} 

public Class2 { 

    public void someMethod() { 
    System.out.println(Class1.ONE + Class1.TWO); 
    } 
} 

Normalmente se esperaría que el compilador en línea En la una y las dos constantes. Sin embargo, ¿este comportamiento está garantizado? ¿Puede implementar en tiempo de ejecución Class2 sin Class1 en el classpath, y esperar que funcione independientemente de los compiladores, o es una optimización de compilador opcional?

EDITAR: ¿Por qué hacer esto? Bueno, tengo una constante que se compartiría entre dos extremos de una aplicación (cliente y servidor sobre RMI) y sería muy conveniente en este caso particular poner la constante en una clase que solo puede estar en un lado de esa división (ya que lógicamente es el que posee ese valor constante) en lugar de tenerlo en una clase de constantes arbitrarias solo porque necesita ser compartida por ambos lados del código. En tiempo de compilación es todo un conjunto de archivos de origen, pero en tiempo de compilación se divide por paquete.

Respuesta

23

Está garantizado a ser tratado como una expresión constante, y garantiza que sea internada por section 15.28 of the JLS:

Una expresión constante de tiempo de compilación es una expresión que denota un valor de tipo primitivo o una cadena que hace no completado bruscamente y se compone utilizando sólo el siguiente:

  • literales de tipo primitivo y literales de tipo String (§3.10.5)
  • Lanza a tipos primitivos y moldes para escribir String
  • Los operadores unarios +, -, ~ y! (Pero no ++ o -)
  • Los operadores multiplicativos *, /, y%
  • Los operadores aditivos + y -
  • ...

...

Las constantes de tiempo de compilación del tipo Cadena siempre se "internan" para compartir instancias únicas, utilizando el método String.intern.

Ahora, eso no dice que esté garantizado que esté en línea. Sin embargo, la sección 13.1 de la especificación dice:

Las referencias a campos que son constantes las variables (§4.12.4) se resuelven en tiempo de compilación con el valor constante que se denota. No se hace referencia a tales un campo constante debe estar presente en el código en un archivo binario (excepto en la clase o interfaz que contiene el campo constante , que tendrá código inicializarlo), y tales constantes campos debe siempre parece haber sido inicializado; el valor inicial predeterminado para el tipo de dicho campo debe ser nunca observado.

En otras palabras, incluso si la expresión en sí no eran una constante, no debería haber ninguna referencia a Class1. Entonces sí, estás bien. Eso no hace que necesariamente garantice que el valor concatenado se usa en el bytecode, pero los bits referenciados anteriormente garantizan que el valor concatenado se interna, por lo que estaría enormemente sorprendido si no solo alineara el valor concatenado . Incluso si no lo hace, tiene la garantía de que funcionará sin Class1.

+1

Gracias Jon, pero eso no es exactamente lo que quise decir. "internado" significa que solo hay una instancia, pero aún podría obtener un NoClassDefError si la clase falta en el tiempo de ejecución si el compilador no lo incluyó realmente. – Yishai

+0

Tal vez estoy ciego, pero ¿dónde dice eso? – gshauger

+0

@gshaugher: ¿Qué parte? –

0

No lo incluirá el compilador sino el intérprete en el tiempo de ejecución y, si es posible, se convertirá en el código de ensamblaje.

No se puede garantizar, porque no todos los intérpretes (JVM) funcionan de la misma manera. Pero las implementaciones más importantes servirán.

Por desgracia, no tienen un enlace a sostener este :(

0

sospecho, pero no saben a ciencia cierta, de que esto va a funcionar, pero no suena como una buena idea.

las formas "normales" de hacer esto son:.

  1. Ponga las constantes en un paquete que es compartida entre el cliente y el servidor de suponer que hay un paquete de este tipo, porque ahí es donde las interfaces van.
  2. Si no hay tal paquete, cree 2 clases con las constantes compartidas: una para el servidor y otra para el cliente.
0

Ver JLS 13.4.9. Si bien no requiere explícitamente que el compilador inline las constantes, sugiere que la compilación condicional y el soporte de las constantes en las declaraciones switch hacen que el compilador siempre tenga constantes en línea.

10

Compilación de que con javac 1.6.0_14 produce el siguiente código de bytes:

public void someMethod(); 
    Code: 
    0: getstatic  #2; //Field java/lang/System.out:Ljava/io/PrintStream; 
    3: ldc  #3; //String ABCDEF 
    5: invokevirtual #4; //Method java/io/PrintStream.println:(Ljava/lang/String;)V 
    8: return 

Así que las cadenas se concatenan en tiempo de compilación y el resultado se incluye en la piscina constante de Clase 2.

+1

+1, gracias por la investigación , pero sabía que el compilador se comportaba de esa manera. Mi pregunta era si la especificación lo garantizaba o si era un comportamiento opcional (y por lo tanto puede volver a afectarlo más tarde). – Yishai

0

Parece que estás codificación de su propia versión de la capacidad incorporada en enum, lo que hace public static final para usted, denominación adecuada a través de name() y toString() (además de tener otras ventajas, pero tal vez con el inconveniente de una mayor huella de memoria).

¿Está utilizando una versión anterior de Java que aún no incluye enumeración?

+0

Mi caso de uso representa las raíces del contexto y las URL, por lo que una cadena recta hacia arriba. El propósito de hacer una constante es evitar duplicarla en caso de que cambie. Así que Enums sería excesivo en este caso. Estoy usando Java 1.5, así que tengo Enums y los uso (tal vez no lo suficiente)) – Yishai

Cuestiones relacionadas