Java条件编译:如何防止编译代码块?
我的项目需要Java 1.6进行编译和运行。 现在我有一个要求,使其与Java 1.5(从市场方面)的工作。 我想要replace方法体(返回types和参数保持不变),使其与Java 1.5编译没有错误。
详细信息:我有一个实用工具类称为OS
封装所有操作系统特定的东西。 它有一个方法
public static void openFile(java.io.File file) throws java.io.IOException { // open the file using java.awt.Desktop ... }
双击打开文件( start
Windows命令或open
Mac OS X命令等效项)。 由于无法使用Java 1.5进行编译,因此我希望在编译过程run32dll
其排除,并用另一种方法replace为Windows调用run32dll
或使用Runtime.exec
open
Mac OS X。
问:我该怎么做? 注释可以帮助吗?
注意:我使用ant,并且可以创build两个java文件OS4J5.java
和OS4J6.java
,它们将包含OS
类与Java 1.5和1.6所需的代码,并将其中一个复制到OS.java
编译之前(或丑陋方式 – 取决于java版本有条件地取代OS.java
的内容),但我不想这样做,如果有另一种方式。
详细阐述:在CI中可以使用ifdef, ifndef
,在Python中没有编译,我可以使用hasattr
或其他东西来检查一个特性,在Common Lisp中我可以使用#+feature
。 有什么类似的Java?
发现这篇文章,但它似乎没有帮助。
任何帮助是极大的赞赏。 KH。
不,在Java中没有任何对条件编译的支持。
通常的计划是在应用程序后面隐藏应用程序的操作系统特定位,然后在运行时检测操作系统types,并使用Class.forName(String)
加载实现。
在你的情况下,没有任何理由不能使用Java 1.6和-source 1.5 -target 1.5
编译OS*
(实际上是你的整个应用程序),然后在工厂方法中获得OS
类(现在将是一个接口)检测java.awt.Desktop
类是否可用并加载正确的版本。
就像是:
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(); } } }
在Gareth提出的界面后面隐藏两个实现类可能是最好的方法。
也就是说,你可以在ant build脚本中引入一种使用replace任务的条件编译。 诀窍是在编译源代码之前在代码中使用通过文本replace来打开/closures的注释,如下所示:
/*{{ 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. */
现在在ant中,当为Java 6编译时,用“* /”replace“IFDEF6”,给出:
/*{{ 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. */
并在为Java 5编译时,replace“IFDEF5”。 请注意,您需要小心使用/*{{
, /*}}
块内的// comments
。
您可以使用reflection进行调用,并使用Java 5编译代码。
例如
Class clazz = Class.forName("java.package.ClassNotFoundInJavav5"); Method method = clazz.getMethod("methodNotFoundInJava5", Class1.class); method.invoke(args1);
你可以捕获任何exception,并回到Java 5上的一些东西。
下面介绍的Ant脚本给出了很好的干净的技巧。
链接: https : //weblogs.java.net/blog/schaefa/archive/2005/01/how_to_do_condi.html
在例子中,
//[ifdef] public byte[] getBytes(String parameterName) throws SQLException { ... } //[enddef]
用Ant脚本
<filterset begintoken="//[" endtoken="]"> <filter token="ifdef" value="${ifdef.token}"/> <filter token="enddef" value="${enddef.token}"/> </filterset>
请参阅上面的链接了解更多详情。
如果您不希望在您的应用程序中有条件启用的代码块,那么预处理器是唯一的方法,您可以看看可以用于Maven和Ant项目的java-comment-preprocessor
PS
我也做了一些例子,如何使用Maven预处理来构buildJEP-238多版本JAR,而不需要重复源代码
我不是一个很好的Java专家,但似乎在Java中的条件编译是支持和容易的。 请阅读:
http://www.javapractices.com/topic/TopicAction.do?Id=64
引用的要点:
条件编译实践用于可选地从类的编译版本中移除代码块。 它使用编译器将忽略任何不可达代码分支的事实。 要实施条件编译,
- 定义一个静态的最终布尔值作为某个类的非私有成员
- 放置代码,这个代码是有条件地编译在评估布尔值的if块中的
- 将boolean的值设置为false以使编译器忽略if块; 否则,保持其值为真
当然,这让我们可以在任何方法中“编译”出大量的代码。 要删除类成员,方法甚至整个类(可能只留下一个存根),您仍然需要一个预处理器。
在Java 9中 ,可以创build多版本的jar文件。 本质上,这意味着你可以制作同一个java文件的多个版本。
编译它们时,需要使用所需的jdk版本编译每个版本的java文件。 接下来,您需要将它们打包成一个如下所示的结构:
+ com + mypackage + Main.class + Utils.class + META-INF + versions + 9 + com + mypackage + Utils.class
在上面的例子中,代码的主要部分是在java 8中编译的,但是对于java 9,还有一个额外的(但是不同的) Utils
类的版本。
当你在java 8 JVM上运行这个代码时,它甚至不会检查META-INF文件夹中的类。 但在Java 9中,它将会,并且会find并使用更新版本的类。