web-dev-qa-db-fra.com

Java compilation conditionnelle: comment empêcher la compilation de morceaux de code?

Mon projet nécessite Java 1.6 pour la compilation et l'exécution. Maintenant, je dois le faire fonctionner avec Java 1.5 (du côté marketing). Je veux remplacer le corps de la méthode (le type de retour et les arguments restent les mêmes) pour la rendre compilable avec Java 1.5 sans erreurs.

Détails: J'ai une classe utilitaire appelée OS qui encapsule toutes les choses spécifiques au système d'exploitation. Il a une méthode

public static void openFile(Java.io.File file) throws Java.io.IOException {
  // open the file using Java.awt.Desktop
  ...
}

pour ouvrir des fichiers comme en double-cliquant (start commande Windows ou open commande Mac OS X équivalente). Puisqu'il ne peut pas être compilé avec Java 1.5, je veux l'exclure lors de la compilation et le remplacer par une autre méthode qui appelle run32dll pour Windows ou open pour Mac OS X à l'aide de Runtime.exec.

Question: Comment puis-je faire cela? Les annotations peuvent-elles aider ici?

Remarque: j'utilise ant, et je peux créer deux Java fichiers OS4J5.Java et OS4J6.Java qui contiendra la classe OS avec le code souhaité pour Java 1.5 et 1.6 et copiez l'un d'eux dans OS.Java avant de compiler (ou d'une manière laide - remplacez le contenu de OS.Java conditionnellement en fonction de Java version) mais je ne veux pas faire ça, s'il y a une autre façon.

Elaborer davantage: en C, je pourrais utiliser ifdef, ifndef, dans Python il n'y a pas de compilation et je pourrais vérifier une fonctionnalité en utilisant hasattr ou autre chose, dans Common LISP je pourrais utiliser #+feature. Existe-t-il quelque chose de similaire pour Java?

Trouvé ce message mais cela ne semble pas être utile.

Toute aide est grandement appréciée. kh.

47
khachik

Non, il n'y a pas de support pour la compilation conditionnelle en Java.

Le plan habituel consiste à masquer les bits spécifiques au système d'exploitation de votre application derrière un Interface, puis à détecter le type de système d'exploitation au moment de l'exécution et à charger l'implémentation à l'aide de Class.forName(String).

Dans votre cas, il n'y a aucune raison pour laquelle vous ne pouvez pas compiler les deux OS* (et en fait toute votre application) en utilisant Java 1.6 avec -source 1.5 -target 1.5 puis dans une méthode d'usine pour mettre la main sur OS classes (qui seraient maintenant une interface) détectent que Java.awt.Desktop La classe est disponible et charge la bonne version.

Quelque chose comme:

 public interface OS {
     void openFile(Java.io.File file) throws Java.io.IOException;
 }

 public class OSFactory {
     public static OS create(){
         try{
             Class.forName("Java.awt.Desktop");
             return new OSJ6();
         }catch(Exception e){
             //fall back
             return new OSJ5();
         }
     }
 }
42
Gareth Davis

Cacher deux classes d'implémentation derrière une interface comme celle proposée par Gareth est probablement la meilleure solution.

Cela dit, vous pouvez introduire une sorte de compilation conditionnelle à l'aide de la tâche de remplacement dans les scripts de génération de fourmi. L'astuce consiste à utiliser des commentaires dans votre code qui sont ouverts/fermés par un remplacement textuel juste avant de compiler la source, comme:

/*{{ Block visible when compiling for Java 6: IFDEF6

public static void openFile(Java.io.File file) throws Java.io.IOException {
  // open the file using Java.awt.Desktop
  ...

/*}} end of Java 6 code. */

/*{{ Block visible when compiling for Java 5: IFDEF5

  // open the file using alternative methods
  ...

/*}} end of Java 5 code. */

maintenant dans ant, lorsque vous compilez pour Java 6, remplacez "IFDEF6" par "* /", donnant:

/*{{ Block visible when compiling for Java 6: */

public static void openFile(Java.io.File file) throws Java.io.IOException {
  // open the file using Java.awt.Desktop
  ...

/*}} end of Java 6 code. */

/*{{ Block visible when compiling for Java 5, IFDEF5

public static void openFile(Java.io.File file) throws Java.io.IOException {
  // open the file using alternative methods
  ...

/*}} end of Java 5 code. */

et lors de la compilation pour Java 5, remplacez "IFDEF5". Notez que vous devez faire attention à utiliser // comments à l'intérieur de /*{{, /*}} blocs.

17
rsp

Le script Ant présenté ci-dessous donne une astuce agréable et propre.

lien: https://weblogs.Java.net/blog/schaefa/archive/2005/01/how_to_do_condi.html

par exemple,

//[ifdef]
public byte[] getBytes(String parameterName)
        throws SQLException {
    ...
}
//[enddef]

avec script Ant

        <filterset begintoken="//[" endtoken="]">
            <filter token="ifdef" value="${ifdef.token}"/>
            <filter token="enddef" value="${enddef.token}"/>
        </filterset>

veuillez cliquer sur le lien ci-dessus pour plus de détails.

7
Youngjae

Vous pouvez effectuer les appels en utilisant la réflexion et compiler le code avec Java 5.

par exemple.

Class clazz = Class.forName("Java.package.ClassNotFoundInJavav5");
Method method = clazz.getMethod("methodNotFoundInJava5", Class1.class);
method.invoke(args1);

Vous pouvez intercepter toutes les exceptions et revenir à quelque chose qui fonctionne sur Java 5.

5
Peter Lawrey

Dans Java 9 il est possible de créer des fichiers jar à plusieurs versions. Cela signifie essentiellement que vous créez plusieurs versions du même fichier Java.

Lorsque vous les compilez, vous compilez chaque version du fichier Java avec la version jdk requise. Ensuite, vous devez les regrouper dans une structure qui ressemble à ceci:

+ com
  + mypackage
    + Main.class
    + Utils.class
+ META-INF
  + versions
    + 9
      + com
        + mypackage
          + Utils.class

Dans l'exemple ci-dessus, la partie principale du code est compilée en Java 8, mais pour Java 9, il existe une version supplémentaire (mais différente) de la Utils classe.

Lorsque vous exécutez ce code sur la machine virtuelle Java Java 8, il ne vérifie même pas les classes dans le dossier META-INF. Mais dans Java 9, il le fera, et trouvera et utilisera la version la plus récente de la classe.

5
bvdb

Je ne suis pas un grand expert Java, mais il semble que la compilation conditionnelle en Java est prise en charge et facile à faire. Veuillez lire:

http://www.javapractices.com/topic/TopicAction.do?Id=64

Citant l'essentiel:

La pratique de compilation conditionnelle est utilisée pour supprimer éventuellement des morceaux de code de la version compilée d'une classe. Il utilise le fait que les compilateurs ignoreront toutes les branches de code inaccessibles. Pour implémenter la compilation conditionnelle,

  • définir une valeur booléenne finale statique en tant que membre non privé d'une classe
  • code de place qui doit être compilé conditionnellement dans un bloc if qui évalue le booléen
  • définissez la valeur du booléen sur false pour que le compilateur ignore le bloc if; sinon, gardez sa valeur vraie

Bien sûr, cela nous permet de "compiler" des morceaux de code dans n'importe quelle méthode. Pour supprimer des membres de classe, des méthodes ou même des classes entières (ne laissant peut-être qu'un stub), vous auriez toujours besoin d'un pré-processeur.

5
gregko

si vous ne voulez pas de blocs de code conditionnellement activés dans votre application, alors un préprocesseur n'est qu'un moyen, vous pouvez jeter un oeil à Java-comment-preprocessor qui peut être utilisé pour les projets maven et ant
p.s.
J'ai aussi fait quelques exemples comment utiliser le prétraitement avec Maven pour construire le JAR multi-version JEP-238 sans duplication des sources

4
Igor Maznitsa

Il y a un nouveau préprocesseur pour Java du framework Manifold . C'est un plugin javac ce qui signifie qu'il est directement intégré avec le Java - pas d'étapes de construction, pas de code objectifs de génération, etc. à gérer.

enter image description here

1
Scott

Générateur de spécialisations primitives Java prend en charge la compilation conditionnelle:

   /* if Windows compilingFor */
   start();
   /* Elif Mac compilingFor */
   open();
   /* endif */

Cet outil a des plugins Maven et Gradle.

0
leventov