Java est-il garanti pour les constantes de chaîne en ligne si elles peuvent être déterminées au moment de la compilation?

StackOverflow https://stackoverflow.com/questions/1406616

  •  05-07-2019
  •  | 
  •  

Question

Considérez ce cas:

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

Généralement, vous vous attendez à ce que le compilateur intègre les constantes ONE et TWO. Cependant, ce comportement est-il garanti? Pouvez-vous déployer au moment de l'exécution Class2 sans Class1 dans le chemin d'accès aux classes et vous attendre à ce qu'il fonctionne indépendamment des compilateurs, ou s'agit-il d'une optimisation facultative du compilateur?

EDIT: Pourquoi diable faire cela? Eh bien, j’ai une constante qui serait partagée entre les deux extrémités d’une application (client et serveur sur RMI) et il serait très pratique dans ce cas particulier de placer la constante sur une classe qui ne peut être que d’un côté de cette division ( comme c'est logiquement celui qui possède cette valeur constante) plutôt que de l'avoir dans une classe de constantes arbitraires simplement parce qu'elle doit être partagée par les deux côtés du code. Au moment de la compilation, c'est tout un ensemble de fichiers source, mais au moment de la construction, il est divisé par paquet.

Était-ce utile?

La solution

Il est garanti d'être traité comme une expression constante et d'être interné par section 15.28 du JLS :

  

Une expression constante à la compilation est   une expression désignant une valeur de   type primitif ou une chaîne qui fait   pas complet brusquement et est composé   en utilisant uniquement les éléments suivants:

     
      
  • Littéraux de type primitif et littéraux de type String (§3.10.5)
  •   
  • Transmet vers des types primitifs et vers un type String
  •   
  • Les opérateurs unaires +, -, ~ et! (mais pas ++ ou -)
  •   
  • Les opérateurs multiplicatifs *, / et%
  •   
  • Les opérateurs additifs + et -
  •   
  • ...
  •   

...

  

Constantes à la compilation de type String   sont toujours " internés " afin de partager   instances uniques, en utilisant la méthode   String.intern.

Maintenant, cela ne signifie pas tout à fait que la garantie soit en ligne. Cependant, la section 13.1 de la spécification dit:

  

Références aux champs constants   les variables (§4.12.4) sont résolues à   compile le temps à la valeur constante   cela est noté. Aucune référence à une telle   un champ constant doit être présent dans   le code dans un fichier binaire (sauf dans   la classe ou l'interface contenant le   champ constant, qui aura le code   pour l'initialiser), et telle constante   les champs doivent toujours sembler avoir été   initialisé; la valeur initiale par défaut   pour le type d'un tel champ doit   ne jamais être observé.

En d'autres termes, même si l'expression elle-même n'est pas une constante , il ne doit y avoir aucune référence à Class1 . Alors oui, ça va. Cela ne ne garantit pas nécessairement que la valeur concaténée est utilisée dans le bytecode, mais les bits référencés précédemment garantissent que la valeur concaténée est internée. Par conséquent, je serais énormément surpris si ce n'est pas juste la valeur concaténée. Même si ce n'est pas le cas, vous êtes assuré que cela fonctionnera sans Class1 .

Autres conseils

Compiler cela avec javac 1.6.0_14 produit le pseudo-code suivant:

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

Les chaînes sont donc concaténées lors de la compilation et le résultat est inclus dans le pool constant de Class2.

Il ne sera pas inséré par le compilateur mais par l'interpréteur au moment de l'exécution et si possible converti en code d'assemblage.

Cela ne peut pas être garanti, car tous les interprètes (JVM) ne fonctionnent pas de la même manière. Mais les implémentations les plus importantes feront l'affaire.

Malheureusement, je n'ai pas de lien pour soutenir ceci :(

Je soupçonne, mais je ne sais pas avec certitude, que cela fonctionnera, mais cela ne semble pas être une bonne idée.

Le " normal " les moyens de le faire sont:

  1. Placez les constantes dans un package partagé entre le client et le serveur. Vraisemblablement, il existe un tel paquet, car c’est là que vont les interfaces.
  2. Si aucun package de ce type n'existe, créez 2 classes avec les constantes partagées: une pour le serveur et une pour le client.

Voir JLS 13.4.9 . . Bien que cela n'exige pas explicitement que les constantes soient intégrées au compilateur, cela indique que la compilation conditionnelle et la prise en charge des constantes dans les instructions switch font que le compilateur utilise toujours des constantes en ligne.

On dirait que vous codez votre propre version de la fonctionnalité intégrée dans enum , ce qui fait final statique pour vous, en nommant correctement via nom ( ) et toString () (en plus d’avoir quelques autres avantages, mais peut-être le désavantage d’une empreinte mémoire plus grande).

Utilisez-vous une ancienne version de Java qui n'inclut pas encore enum?

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top