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

mon projet nécessite Java 1.6 Pour la compilation et l'exécution. Maintenant, J'ai l'obligation de 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 faire compiler avec Java 1.5 sans erreurs.

Détails: j'ai une classe utilitaire appelé OS qui encapsule tous les systèmes d'exploitation spécifiques choses. 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 avec un double-clic ( start Windows command ou open Mac OS X command equivalent). Comme il ne peut pas être compilé avec Java 1.5, je veux l'exclure pendant la compilation et le remplacer par une autre méthode qui appelle run32dll pour Windows ou open pour Mac OS X en utilisant Runtime.exec .

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

Note: j'utilise ant, et je peux faire deux fichiers java OS4J5.java et OS4J6.java qui contiendra la OS de classe avec le code désiré pour Java 1.5 et 1.6 et la copie de l'un d'entre eux de OS.java avant la compilation (ou un vilain moyen - remplacer le contenu de OS.java conditionnellement selon la version java) mais je ne veux pas le faire, si il ya une autre façon.

développer plus: en C je pourrais utiliser ifdef, ifndef , en Python il n'y a pas de compilation et je pourrais vérifier une fonctionnalité en utilisant hasattr ou autre chose, en Lisp commun je pourrais utiliser #+feature . Y a-t-il quelque chose de similaire pour Java?

Trouvé ce post mais il ne semble pas être utile.

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

41
demandé sur Community 2010-12-24 14:44:16

7 réponses

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

le plan habituel est de cacher les bits spécifiques de votre application derrière un Interface , puis de détecter le type D'OS à l'exécution et de charger l'implémentation en utilisant Class.forName(String) .

dans votre cas, il n'y a pas de raison pour que vous ne puissiez pas compiler les deux OS* (et infacter votre application entière) en utilisant Java 1.6 avec -source 1.5 -target 1.5 puis dans une méthode d'usine pour mettre la main sur Les classes OS (qui seraient maintenant une interface) détectent que la classe java.awt.Desktop est disponible et chargent la version correcte.

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();
         }
     }
 }
38
répondu Gareth Davis 2014-04-17 11:02:09

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

cela dit, Vous pouvez introduire une sorte de compilation conditionnelle en utilisant la tâche remplacer dans les scripts de construction ant. Le truc est d'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, quand vous compilez pour Java 6, Remplacer "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, remplacer"IFDEF5". Notez que vous devez être prudent d'utiliser // comments à l'intérieur des blocs /*{{ , /*}} .

17
répondu rsp 2010-12-24 12:35:55

le script de fourmi présenté ci-dessous donne le tour agréable et propre.

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

dans l'exemple,

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

avec le script Ant

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

s'il vous plaît cliquer sur le lien ci-dessus pour plus de détails.

6
répondu Youngjae 2013-12-27 08:41:09

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

p.ex.

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

vous pouvez attraper toutes les exceptions et revenir à quelque chose qui fonctionne sur Java 5.

5
répondu Peter Lawrey 2010-12-24 11:53:09

Je ne suis pas un grand expert en Java, mais il semble que la compilation conditionnelle en Java soit supportée et facile à faire. S'il vous plaît lire:

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

citant l'essentiel:

la pratique de compilation conditionnelle est utilisée pour supprimer optionnellement des morceaux de code de la version compilée d'une classe. Il utilise le fait que les compilateurs ignorez toutes les branches inaccessibles du code. Pour mettre en œuvre la compilation conditionnelle,

  • définir un static final boolean valeur en tant que non-membre privé de certains de la classe
  • code de lieu qui doit être compilé conditionnellement dans un bloc de SI qui évalue le booléen
  • définit la valeur du booléen à false pour amener le compilateur à ignorer le bloc if; sinon, garde sa valeur comme true

bien sûr, cela nous permet de "compiler" les morceaux de code à l'intérieur de 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 talon), vous auriez encore besoin d'un pré-processeur.

5
répondu gregko 2016-10-02 12:54:30

dans java 9 il est possible de créer des fichiers jar multi-versions. Essentiellement, cela signifie que vous faites plusieurs versions du même fichier java.

quand vous les compilez, vous compilez chaque version du fichier java avec la version JDK requise. Ensuite, vous devez les empaqueter 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é en java 8, mais pour java 9, une version supplémentaire (mais différente) de la classe Utils .

quand vous lancez ce code sur la Java 8 JVM, il ne vérifiera même pas les classes dans le dossier META-INF. Mais en java 9, Il trouvera et utilisera la version la plus récente de la classe.

5
répondu bvdb 2017-06-28 09:29:01

si vous ne voulez pas de blocs de code activés conditionnellement dans votre application, alors un préprocesseur n'est qu'un moyen, vous pouvez jeter un oeil à java-comment-préprocesseur qui peut être utilisé pour les projets maven et ant

p. S.

en outre, j'ai fait un certain exemple comment utiliser prétraitement avec Maven pour construire JEP-238 JAR multi-version sans duplication des sources

4
répondu Igor Maznitsa 2017-09-22 14:29:20