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