Java条件编译:如何防止代码块被编译?

时间:2010-12-24 11:44:16

标签: java conditional-compilation

我的项目需要Java 1.6进行编译和运行。现在我需要使用Java 1.5(来自营销方面)。我想替换方法体(返回类型和参数保持不变),以便使用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调用open for Windows或Runtime.exec for Mac OS X的其他方法。

问题:我该怎么做?注释可以帮助吗?

注意:我使用ant,我可以生成两个java文件OS4J5.javaOS4J6.java,它们将包含OS类,其中包含Java 1.5和1.6所需的代码,并且复制其中一个在编译之前它们到OS.java(或者是一种丑陋的方式 - 根据java版本有条件地替换OS.java的内容)但是我不想这样做,如果有另一种方式的话。

详细说明:在C中,我可以使用ifdef, ifndef,在Python中没有编译,我可以使用hasattr或其他东西检查一个功能,在Common Lisp中我可以使用#+feature。 Java有类似的东西吗?

找到this post,但似乎没有帮助。

非常感谢任何帮助。 KH。

9 个答案:

答案 0 :(得分:42)

在Java中没有任何对条件编译的支持。

通常的计划是将应用程序的操作系统特定位隐藏在Interface之后,然后在运行时检测操作系统类型并使用Class.forName(String)加载实现。

在你的情况下,你没有理由不能使用带有OS*的Java 1.6来编译-source 1.5 -target 1.5(以及你的整个应用程序),然后在工厂方法中获取{{ 1}}类(现在是一个接口)检测OS类是否可用并加载正确的版本。

类似的东西:

java.awt.Desktop

答案 1 :(得分:17)

隐藏Gareth提出的接口背后的两个实现类可能是最好的方法。

也就是说,你可以在ant build脚本中使用replace任务引入一种条件编译。诀窍是在代码中使用注释,这些注释在编译源代码之前由文本替换打开/关闭,例如:

/*{{ 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编译时,用“* /”替换“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时,替换“IFDEF5”。请注意,您需要谨慎使用// comments/*{{块内的/*}}

答案 2 :(得分:6)

下面介绍的Ant脚本提供了很好的清洁技巧。

link: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>

请点击上面的链接了解更多详情。

答案 3 :(得分:5)

您可以使用反射进行调用,并使用Java 5编译代码。

e.g。

Class clazz = Class.forName("java.package.ClassNotFoundInJavav5");
Method method = clazz.getMethod("methodNotFoundInJava5", Class1.class);
method.invoke(args1);

你可以捕获任何异常并回归到适用于Java 5的东西。

答案 4 :(得分:5)

我不是一个优秀的Java专家,但似乎支持Java中的条件编译并且很容易。请阅读:

http://www.javapractices.com/topic/TopicAction.do?Id=64

引用要点:

  

条件编译实践用于可选地从类的编译版本中删除代码块。它使用编译器将忽略任何无法访问的代码分支的事实。   要实现条件编译,

     
      
  • 将静态最终布尔值定义为某个类的非私有成员
  •   
  • 将有条件编译的代码放在if块中,该块用于计算布尔值
  •   
  • 将布尔值设置为false,以使编译器忽略if块;否则,保持其值为真
  •   

当然,这让我们可以编译出#34;任何方法内的代码块。要删除类成员,方法甚至整个类(可能只留下存根),您仍然需要预处理器。

答案 5 :(得分:5)

java 9 中,可以创建多版本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中它将会发现并使用更新版本的类。

答案 6 :(得分:4)

如果您不想在应用程序中使用有条件启用的代码块,那么只需预处理器,您可以查看可用于maven和ant项目的java-comment-preprocessor。 附:
我也做了some example how to use preprocessing with Maven to build JEP-238 multi-version JAR without duplication of sources

答案 7 :(得分:1)

Preprocessor中有一个新的Java Manifold framework。这是一个Javac 插件,这意味着它与Java编译器直接集成了 -没有构建步骤,没有代码生成目标等要管理。

enter image description here

答案 8 :(得分:0)

Java Primitive Specializations Generator支持条件编译:

   /* if Windows compilingFor */
   start();
   /* elif Mac compilingFor */
   open();
   /* endif */

此工具具有Maven和Gradle插件。