Compilación condicional de Java: ¿cómo evitar que se compilen los fragmentos de código?
conditional-compilation (7)
El script Ant que se presenta a continuación brinda un truco agradable y limpio.
enlace: https://weblogs.java.net/blog/schaefa/archive/2005/01/how_to_do_condi.html
por ejemplo,
//[ifdef]
public byte[] getBytes(String parameterName)
throws SQLException {
...
}
//[enddef]
con script Ant
<filterset begintoken="//[" endtoken="]">
<filter token="ifdef" value="${ifdef.token}"/>
<filter token="enddef" value="${enddef.token}"/>
</filterset>
por favor vaya al enlace de arriba para más detalles.
Mi proyecto requiere Java 1.6 para compilación y ejecución. Ahora tengo el requisito de hacerlo funcionar con Java 1.5 (desde el lado de marketing). Quiero reemplazar el cuerpo del método (el tipo de retorno y los argumentos siguen siendo los mismos) para hacerlo compilar con Java 1.5 sin errores.
Detalles: Tengo una clase de utilidad llamada OS
que encapsula todas las cosas específicas del sistema operativo. Tiene un método
public static void openFile(java.io.File file) throws java.io.IOException {
// open the file using java.awt.Desktop
...
}
para abrir archivos como hacer doble clic ( start
el comando de Windows o open
comando Mac OS X equivalente). Como no se puede compilar con Java 1.5, quiero excluirlo durante la compilación y reemplazarlo por otro método que llame a run32dll
para Windows o open
para Mac OS X usando Runtime.exec
.
Pregunta: ¿Cómo puedo hacer eso? ¿Pueden las anotaciones ayudar aquí?
Nota: Yo uso hormiga, y puedo hacer dos archivos java OS4J5.java
y OS4J6.java
que contendrán la clase OS
con el código deseado para Java 1.5 y 1.6 y copiar uno de ellos a OS.java
antes de compilar (o un feo manera - reemplace el contenido de OS.java
condicionalmente dependiendo de la versión de Java) pero no quiero hacer eso, si hay otra manera.
Elaborando más: en CI podría usar ifdef, ifndef
, en Python no hay compilación y podría verificar una característica usando hasattr
o algo más, en Common Lisp podría usar la función #+feature
. ¿Hay algo similar para Java?
Encontré esta publicación pero no parece ser útil.
Cualquier ayuda es muy apreciada. kh.
En java 9 es posible crear archivos jar de liberación múltiple. Esencialmente significa que usted hace múltiples versiones del mismo archivo java.
Cuando los compila, compila cada versión del archivo java con la versión jdk requerida. A continuación, debe empaquetarlos en una estructura que se vea así:
+ com
+ mypackage
+ Main.class
+ Utils.class
+ META-INF
+ versions
+ 9
+ com
+ mypackage
+ Utils.class
En el ejemplo anterior, la parte principal del código se compila en java 8, pero para java 9 hay una versión adicional (pero diferente) de la clase Utils
.
Cuando ejecuta este código en java 8 JVM, ni siquiera verifica las clases en la carpeta META-INF. Pero en java 9 lo hará, y encontrará y usará la versión más reciente de la clase.
No soy un gran experto en Java, pero parece que la compilación condicional en Java es compatible y fácil de hacer. Por favor lee:
http://www.javapractices.com/topic/TopicAction.do?Id=64
Citando la esencia:
La práctica de compilación condicional se utiliza para eliminar opcionalmente trozos de código de la versión compilada de una clase. Utiliza el hecho de que los compiladores ignorarán cualquier rama de código inalcanzable. Para implementar la compilación condicional,
- define un valor booleano final estático como miembro no privado de alguna clase
- código de lugar que debe compilarse condicionalmente en un bloque if que evalúa el valor booleano
- establecer el valor de booleano en falso para hacer que el compilador ignore el bloque if; de lo contrario, mantenga su valor como verdadero
Por supuesto, esto nos permite "compilar" trozos de código dentro de cualquier método. Para eliminar miembros de la clase, métodos o incluso clases enteras (tal vez dejando solo un talón) aún necesitarías un preprocesador.
No, no hay soporte para la compilación condicional en Java.
El plan habitual es ocultar los bits específicos del sistema operativo de su aplicación detrás de una Interface
y luego detectar el tipo de sistema operativo en tiempo de ejecución y cargar la implementación utilizando Class.forName(String)
.
En su caso, no hay ninguna razón por la que no pueda compilar ambos OS*
(y de hecho toda su aplicación) usando Java 1.6 con -source 1.5 -target 1.5
luego en un método de fábrica para obtener clases de OS
(que ahora sería una interfaz) detecta que la clase java.awt.Desktop
está disponible y carga la versión correcta.
Algo como:
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();
}
}
}
Ocultar las dos clases de implementación detrás de una interfaz propuesta por Gareth es probablemente la mejor manera de hacerlo.
Dicho esto, puede introducir un tipo de compilación condicional utilizando la tarea de reemplazo en los scripts de compilación de ant. El truco consiste en utilizar comentarios en su código que se abren / cierran mediante un reemplazo de texto justo antes de compilar la fuente, como:
/*{{ 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. */
ahora en ant, cuando compile para Java 6, reemplace "IFDEF6" por "* /", dando:
/*{{ 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. */
y al compilar para Java 5, reemplace "IFDEF5". Tenga en cuenta que debe tener cuidado de usar // comments
dentro de los bloques /*{{
, /*}}
.
Puede hacer las llamadas usando reflexión y compilar el código con Java 5.
p.ej
Class clazz = Class.forName("java.package.ClassNotFoundInJavav5");
Method method = clazz.getMethod("methodNotFoundInJava5", Class1.class);
method.invoke(args1);
Puede capturar cualquier excepción y recurrir a algo que funciona en Java 5.
Si no quiere bloques de código condicionalmente habilitados en su aplicación, entonces un preprocesador es solo una forma, puede echar un vistazo a java-comment-preprocessor que puede usarse tanto para proyectos maven como para ant.
PD
también he dado un ejemplo de cómo usar el preprocesamiento con Maven para construir JAR multi-versión JEP-238 sin duplicación de fuentes