Compilación de Java: ¿hay alguna manera de decirle al compilador que ignore partes de mi código?

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

  •  09-06-2019
  •  | 
  •  

Pregunta

Mantengo una aplicación Java Swing.

Para compatibilidad con versiones anteriores de Java 5 (para máquinas Apple), mantenemos dos bases de código, una que utiliza funciones de Java 6 y otra sin esas funciones.

El código es prácticamente el mismo, excepto por 3 o 4 clases que utilizan funciones de Java 6.

Deseo solo mantener 1 base de código.¿Hay alguna manera durante la compilación de hacer que el compilador de Java 5 "ignore" algunas partes de mi código?

No deseo simplemente comentar/descomentar partes de mi código, dependiendo de la versión de mi compilador de Java.

¿Fue útil?

Solución

Suponiendo que las clases tienen una funcionalidad similar con 1.5 vs.6.0 diferencias en la implementación, puede fusionarlas en una clase.Luego, sin editar la fuente para comentar/descomentar, puede confiar en la optimización que siempre realiza el compilador.Si una expresión if es siempre falsa, el código de la declaración if no se incluirá en la compilación.

Puede crear una variable estática en una de sus clases para determinar qué versión desea ejecutar:

public static final boolean COMPILED_IN_JAVA_6 = false;

Y luego haga que las clases afectadas verifiquen esa variable estática y coloquen las diferentes secciones de código en una declaración if simple.

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

Luego, cuando quieras compilar la otra versión, solo tienes que cambiar esa variable y volver a compilar.Puede agrandar el archivo Java, pero consolidará su código y eliminará cualquier duplicación de código que tenga.Su editor puede quejarse de código inalcanzable o lo que sea, pero el compilador debería ignorarlo felizmente.

Otros consejos

Las sugerencias sobre el uso de cargadores de clases personalizados y código comentado dinámicamente son un poco incrédulas cuando se trata de mantenimiento y preservación de la cordura de cualquier pobre alma que retome el proyecto después de que usted se adentra en nuevos pastos.

La solución es fácil.Divida las clases afectadas en dos proyectos separados e independientes: asegúrese de que los nombres de los paquetes sean los mismos y simplemente compílelos en archivos jar que luego podrá consumir en su proyecto principal.Si mantiene los mismos nombres de paquetes y las mismas firmas de métodos, no hay problemas: simplemente coloque la versión del jar que necesite en su script de implementación.Supongo que ejecuta scripts de compilación separados o tiene objetivos separados en el mismo script: ant y maven pueden manejar fácilmente la captura condicional de archivos y su copia.

Creo que el mejor enfoque aquí probablemente sea utilizar scripts de compilación.Puede tener todo su código en una ubicación y, al elegir qué archivos incluir y cuáles no incluir, puede elegir qué versión de su código compilar.Tenga en cuenta que esto puede no ser útil si necesita un control más detallado que por archivo.

Probablemente pueda refactorizar su código para que la compilación condicional realmente no sea necesaria, solo la carga de clases condicional.Algo como esto:

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

}

Esto podría suponer un gran esfuerzo dependiendo de cuántos fragmentos de código específicos de la versión tenga.

Mantenga una raíz fuente "maestra" que se compila en JDK 5.Agregue una segunda raíz de origen paralela que debe compilarse en JDK 6 o superior.(No debe haber superposición, es decirno hay clases presentes en ambos.) Utilice una interfaz para definir el punto de entrada entre los dos y un poquito de reflexión.

Por ejemplo:

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

Puede configurarlos como proyectos separados en un IDE usando diferentes JDK, etc.El punto es que la raíz principal se puede compilar de forma independiente y está muy claro qué se puede usar en cada raíz, mientras que si se intenta compilar diferentes partes de una única raíz por separado es muy fácil "filtrar" accidentalmente el uso de JDK 6. en los archivos equivocados.

En lugar de usar Class.forName de esta manera, también puede usar algún tipo de sistema de registro de servicios: java.util.ServiceLoader (¡si main pudiera usar JDK 6 y quisiera soporte opcional para JDK 7!), NetBeans Lookup, Spring, etc.etc.

Se puede utilizar la misma técnica para crear soporte para una biblioteca opcional en lugar de un JDK más nuevo.

En realidad no, pero existen soluciones.Verhttp://forums.sun.com/thread.jspa?threadID=154106&messageID=447625

Dicho esto, debes limitarte a tener al menos una versión de archivo para Java 5 y otra para Java 6, e incluirlas mediante una compilación o creación según corresponda.Pegarlo todo en un archivo grande e intentar que el compilador 5 ignore cosas que no entiende no es una buena solución.

HT

--nikki--

Esto hará que todos los puristas de Java se estremezcan (lo cual es divertido, je, je), pero yo usaría el preprocesador de C y pondría #ifdefs en mi fuente.Un archivo MAKE, un archivo rake o cualquier otro que controle su compilación tendría que ejecutar cpp para crear archivos temporales que alimenten el compilador.No tengo idea si se podría hacer que una hormiga haga esto.

Si bien parece que stackoverflow será el lugar para todas las respuestas, se podría pensar que nadie está mirando http://www.javaranch.com por la sabiduría de Java.Me imagino que esta cuestión se habrá tratado allí, probablemente hace mucho tiempo.

Depende de las funciones de Java 6 que desee utilizar.Para algo tan simple como agregar clasificadores de filas a JTables, puedes probar en tiempo de ejecución:

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
}

Este código debe compilarse con Java 6, pero se puede ejecutar con cualquier versión (no se hace referencia a clases nuevas).

EDITAR:para ser más correcto, funcionará con cualquier versión desde 1.3 (según esta página).

Puede realizar toda la compilación exclusivamente en Java6 y luego usar System.getProperty("java.version") para ejecutar condicionalmente la ruta del código Java5 o Java6.

Puede tener código solo Java6 en una clase y la clase se ejecutará bien en Java5 siempre que no se ejecute la ruta del código solo Java6.

Este es un truco que se utiliza para escribir subprogramas que se ejecutarán desde la antigua MSJVM hasta las nuevas JVM de complementos de Java.

No existe un precompilador en Java.Por lo tanto, no hay forma de hacer un #ifdef como en C.Crear scripts sería la mejor manera.

Puede obtener una compilación condicional, pero no muy bien: javac ignorará el código inalcanzable.Por lo tanto, si estructuró su código correctamente, puede hacer que el compilador ignore partes de su código.Para usar esto correctamente, también deberá pasar los argumentos correctos a javac para que no informe código inalcanzable como errores y se niegue a compilar :-)

La solución final pública estática mencionada anteriormente tiene un beneficio adicional que el autor no mencionó: según tengo entendido, el compilador la reconocerá en el momento de la compilación y compilará cualquier código que esté dentro de una declaración if que se refiera a esa variable final.

Entonces creo que esa es la solución exacta que estabas buscando.

Una solución sencilla podría ser:

  • Coloque las clases divergentes fuera de su ruta de clases normal.
  • Escriba un cargador de clases personalizado simple e instálelo en main como predeterminado.
  • Para todas las clases, excepto las 5/6, el cargador de cass puede diferir a su padre (el cargador de clases normal del sistema)
  • Para los 5/6 (que deberían ser los únicos que el padre no puede encontrar) puede decidir cuáles usar a través de la propiedad 'os.name' o una propia.

Puede utilizar la API de reflexión.coloque todo su código 1.5 en una clase y la API 1.6 en otra.En su script ant cree dos objetivos, uno para 1.5 que no compilará la clase 1.6 y otro para 1.6 que no compilará la clase para 1.5.en su código verifique su versión de Java y cargue la clase apropiada usando la reflexión de esa manera javac no se quejará de funciones faltantes.Así es como puedo compilar mis aplicaciones MRJ (Mac Runtime para Java) en Windows.

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top