54

我的项目需要 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
  ...
}

双击打开文件(startWindows 命令或openMac OS X 命令等效)。由于它不能用 Java 1.5 编译,我想在编译期间排除它,并用另一种调用run32dllWindows 或openMac OS X 的方法替换它,使用Runtime.exec.

问题:我该怎么做?注释可以在这里提供帮助吗?

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

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

找到了这篇文章,但似乎没有帮助。

任何帮助是极大的赞赏。kh。

4

10 回答 10

43

不,Java 中不支持条件编译。

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

在您的情况下,您没有理由不能OS*使用 Java 1.6 编译两者(实际上是整个应用程序),-source 1.5 -target 1.5然后在工厂方法中获取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();
         }
     }
 }
于 2010-12-24T11:47:46.223 回答
18

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

也就是说,您可以使用 ant 构建脚本中的 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/*{{,/*}}块内使用。

于 2010-12-24T12:35:55.890 回答
6

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

例如

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

您可以捕获任何异常并回退到适用于 Java 5 的某些东西。

于 2010-12-24T11:53:09.057 回答
6

下面介绍的 Ant 脚本提供了漂亮而干净的技巧。

链接:https ://weblogs.java.net/blog/schaefa/archive/2005/01/how_to_do_condi.html

例如,

//[ifdef]
public byte[] getBytes(String parameterName)
        throws SQLException {
    ...
}
//[enddef]

用蚂蚁脚本

        <filterset begintoken="//[" endtoken="]">
            <filter token="ifdef" value="${ifdef.token}"/>
            <filter token="enddef" value="${enddef.token}"/>
        </filterset>

请转到上面的链接以获取更多详细信息。

于 2013-12-27T08:41:09.777 回答
6

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 中,它将找到并使用该类的更新版本。

于 2017-06-20T14:32:44.273 回答
5

如果您不想在应用程序中启用有条件的代码块,那么预处理器是唯一的方法,您可以查看可用于 maven 和 ant 项目的java-comment-preprocessor
ps
我也做了一些示例如何使用 Maven 预处理构建 JEP-238 多版本 JAR,无需重复源

于 2015-03-21T11:58:20.240 回答
5

我不是那么出色的 Java 专家,但似乎支持 Java 中的条件编译并且很容易做到。请阅读:

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

引用要点:

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

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

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

于 2016-05-27T23:21:43.113 回答
5

Manifold 框架有一个新的 Java预处理器。它是一个 javac插件,这意味着它直接与 Java 编译器集成——没有构建步骤、没有代码生成目标等需要管理。

在此处输入图像描述

于 2019-07-30T05:23:09.057 回答
0

Java Primitive Specializations Generator支持条件编译:

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

这个工具有 Maven 和 Gradle 插件。

于 2019-04-08T18:34:35.593 回答
0

嗨,当我在 Java SDK abd Android 之间共享库并且在两种环境中都使用图形时,我遇到了类似的问题,所以基本上我的代码必须同时使用 java.awt.Graphics 和 android.graphics.Canvas,但我没有想要复制几乎所有代码。我的解决方案是使用包装器,因此我以间接方式访问 graphisc API,并且我可以更改几个导入,以导入我想要编译项目的包装器。这些项目有一些锥形阴影,一些是独立的,但除了几个包装器等之外,没有任何重复的东西。我认为这是我能做的最好的事情。

于 2021-01-13T21:54:22.517 回答