Compilazione Java: esiste un modo per dire al compilatore di ignorare parti del mio codice?

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

  •  09-06-2019
  •  | 
  •  

Domanda

Mantengo un'applicazione Java Swing.

Per compatibilità con Java 5 (per macchine Apple), manteniamo due basi di codice, una che utilizza funzionalità di Java 6, un'altra senza tali funzionalità.

Il codice è sostanzialmente lo stesso, ad eccezione di 3-4 classi che utilizzano le funzionalità di Java 6.

Desidero mantenere solo 1 codebase.Esiste un modo durante la compilazione per fare in modo che il compilatore Java 5 "ignori" alcune parti del mio codice?

Non desidero semplicemente commentare/decommentare parti del mio codice, a seconda della versione del mio compilatore Java.

È stato utile?

Soluzione

Supponendo che le classi abbiano funzionalità simili con 1.5 vs.6.0 differenze nell'implementazione potresti unirle in un'unica classe.Quindi, senza modificare la fonte per commentare/decommentare, puoi fare affidamento sull'ottimizzazione che il compilatore esegue sempre.Se un'espressione if è sempre falsa, il codice nell'istruzione if non verrà incluso nella compilazione.

Puoi creare una variabile statica in una delle tue classi per determinare quale versione desideri eseguire:

public static final boolean COMPILED_IN_JAVA_6 = false;

E poi chiedi alle classi interessate di controllare quella variabile statica e di inserire le diverse sezioni di codice in una semplice istruzione if

if (VersionUtil.COMPILED_IN_JAVA_6) {
  // Java 6 stuff goes here
} else {
  // Java 1.5 stuff goes here
}

Quindi quando vuoi compilare l'altra versione devi solo cambiare quella variabile e ricompilare.Potrebbe rendere il file Java più grande ma consoliderà il tuo codice ed eliminerà qualsiasi duplicazione del codice che hai.Il tuo editor potrebbe lamentarsi di codice irraggiungibile o altro, ma il compilatore dovrebbe ignorarlo beatamente.

Altri suggerimenti

I suggerimenti sull'utilizzo di caricatori di classi personalizzati e codice commentato dinamicamente sono un po' increduli quando si tratta di manutenzione e preservazione della sanità mentale di qualunque povera anima raccolga il progetto dopo che ti sei trascinato verso nuovi pascoli.

La soluzione è semplice.Estrai le classi interessate in due progetti separati e indipendenti: assicurati che i nomi dei pacchetti siano gli stessi e compila semplicemente in jar che puoi quindi utilizzare nel tuo progetto principale.Se mantieni gli stessi nomi dei pacchetti e le stesse firme dei metodi, nessun problema: inserisci semplicemente la versione del jar di cui hai bisogno nello script di distribuzione.Presumo che tu esegua script di build separati o abbia obiettivi separati nello stesso script: Ant e Maven possono entrambi gestire facilmente l'acquisizione condizionale dei file e la loro copia.

Penso che l'approccio migliore qui sia probabilmente quello di utilizzare gli script di build.Puoi avere tutto il tuo codice in un'unica posizione e, scegliendo quali file includere e quali non includere, puoi scegliere quale versione del tuo codice compilare.Tieni presente che questo potrebbe non essere d'aiuto se hai bisogno di un controllo più preciso rispetto a quello per file.

Probabilmente puoi rifattorizzare il tuo codice in modo che la compilazione condizionale non sia davvero necessaria, ma solo il caricamento delle classi condizionale.Qualcosa come questo:

public interface Opener{

public void open(File f);

 public static class Util{
        public Opener getOpener(){
          if(System.getProperty("java.version").beginsWith("1.5")){
           return new Java5Opener();
          }
          try{ 
            return new Java6Opener();
           }catch(Throwable t){
            return new Java5Opener();
           }
        }
 }

}

Questo potrebbe richiedere molto impegno a seconda di quante parti di codice specifiche della versione hai.

Mantieni una root di origine "master" creata con JDK 5.Aggiungi una seconda root di origine parallela che deve essere compilata con JDK 6 o versione successiva.(Non dovrebbero esserci sovrapposizioni, ad es.nessuna classe presente in entrambi.) Utilizzare un'interfaccia per definire il punto di ingresso tra i due e un po' di riflessione.

Per esempio:

---%<--- main/RandomClass.java
// ...
if (...is JDK 6+...) {
    try {
        JDK6Interface i = (JDK6Interface)
            Class.forName("JDK6Impl").newInstance();
        i.browseDesktop(...);
    } catch (Exception x) {
        // fall back...
    }
}
---%<--- main/JDK6Interface.java
public interface JDK6Interface {
    void browseDesktop(URI uri);
}
---%<--- jdk6/JDK6Impl.java
public class JDK6Impl implements JDK6Interface {
    public void browseDesktop(URI uri) {
        java.awt.Desktop.getDesktop().browse(uri);
    }
}
---%<---

È possibile configurarli come progetti separati in un IDE utilizzando JDK diversi, ecc.Il punto è che la root principale può essere compilata in modo indipendente ed è molto chiaro cosa puoi usare in quale root, mentre se provi a compilare parti diverse di una singola root separatamente è troppo facile che accidentalmente "trapeli" l'utilizzo di JDK 6 nei file sbagliati.

Invece di utilizzare Class.forName in questo modo, puoi anche utilizzare una sorta di sistema di registrazione del servizio: java.util.ServiceLoader (se main potesse utilizzare JDK 6 e desideri supporto opzionale per JDK 7!), NetBeans Lookup, Spring, ecc.eccetera.

La stessa tecnica può essere utilizzata per creare il supporto per una libreria opzionale anziché per un JDK più recente.

Non proprio, ma ci sono soluzioni alternative.Vederehttp://forums.sun.com/thread.jspa?threadID=154106&messageID=447625

Detto questo, dovresti limitarti ad avere almeno una versione del file per Java 5 e una per Java 6 e includerle tramite build o make a seconda dei casi.Mettere tutto in un unico grande file e cercare di fare in modo che il compilatore per 5 ignori le cose che non capisce non è una buona soluzione.

HTH

--nikki-

Questo farà rabbrividire tutti i puristi di Java (il che è divertente, eh eh) ma userei il preprocessore C, inserirei #ifdefs nel mio sorgente.Un makefile, rakefile o qualunque cosa controlli la tua build, dovrebbe eseguire cpp per creare un file temporaneo da alimentare al compilatore.Non ho idea se sia possibile costringere una formica a fare questo.

Mentre StackOverflow sembra che lo sarà IL posto per tutte le risposte, potresti immaginare che nessuno stia guardando in giro http://www.javaranch.com per la saggezza di Java.Immagino che questa domanda sia stata affrontata lì, probabilmente molto tempo fa.

Dipende dalle funzionalità Java 6 che desideri utilizzare.Per una cosa semplice come aggiungere selezionatori di righe a JTables, puoi effettivamente testare in fase di esecuzione:

private static final double javaVersion =
         Double.parseDouble(System.getProperty("java.version").substring(0, 3));
private static final boolean supportsRowSorter =
         (javaVersion >= 1.6);

//...

if (supportsRowSorter) {
    myTable.setAutoCreateRowSorter(true);
} else {
    // not supported
}

Questo codice deve essere compilato con Java 6, ma può essere eseguito con qualsiasi versione (non viene fatto riferimento a nuove classi).

MODIFICARE:per essere più corretti, funzionerà con qualsiasi versione a partire dalla 1.3 (secondo questa pagina).

È possibile eseguire tutta la compilazione esclusivamente su Java6 e quindi utilizzare System.getProperty("java.version") per eseguire in modo condizionale il percorso del codice Java5 o Java6.

È possibile avere codice solo Java6 in una classe e la classe funzionerà correttamente su Java5 purché il percorso del codice solo Java6 non venga eseguito.

Questo è un trucco utilizzato per scrivere applet che verranno eseguiti sul vecchio MSJVM fino alle nuovissime JVM plug-in Java.

Non esiste un precompilatore in Java.Quindi, non c'è modo di fare un #ifdef come in C.Costruire script sarebbe il modo migliore.

Puoi ottenere la compilazione condizionale, ma non molto bene: javac ignorerà il codice irraggiungibile.Pertanto, se hai strutturato il tuo codice correttamente, puoi fare in modo che il compilatore ignori parti del tuo codice.Per usarlo correttamente, dovresti anche passare gli argomenti corretti a javac in modo che non segnali codice irraggiungibile come errori e si rifiuti di compilare :-)

La soluzione finale statica pubblica menzionata sopra ha un ulteriore vantaggio che l'autore non ha menzionato: a quanto ho capito, il compilatore lo riconoscerà in fase di compilazione e compilerà qualsiasi codice presente all'interno di un'istruzione if che si riferisce a quella variabile finale.

Quindi penso che sia la soluzione esatta che stavi cercando.

Una soluzione semplice potrebbe essere:

  • Posiziona le classi divergenti al di fuori del normale percorso di classe.
  • Scrivi un semplice classloader personalizzato e installalo in main come predefinito.
  • Per tutte le classi tranne quelle 5/6 il cassloader può rimandare al suo genitore (il normale classloader del sistema)
  • Per i 5/6 (che dovrebbero essere gli unici che non possono essere trovati dal genitore) si può decidere quale utilizzare tramite la proprietà 'os.name' o una propria.

Puoi utilizzare l'API di riflessione.metti tutto il tuo codice 1.5 in una classe e 1.6 API in un'altra.Nel tuo script ant crea due target uno per 1.5 che non compilerà la classe 1.6 e uno per 1.6 che non compilerà la classe per 1.5.nel tuo codice controlla la tua versione Java e carica la classe appropriata utilizzando la riflessione in questo modo Javac non si lamenterà delle funzioni mancanti.Ecco come posso compilare le mie applicazioni MRJ (Mac Runtime for Java) su Windows.

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top