Java编译 - 有没有办法告诉编译器忽略我的部分代码?

时间:2008-09-16 16:20:18

标签: java swing compilation

我维护一个Java Swing应用程序。

为了向后兼容java 5(对于Apple机器),我们维护了两个代码库,一个使用Java 6中的功能,另一个没有这些功能。

除了使用Java 6功能的3-4个类之外,代码大致相同。

我希望维护1个代码库。在编译期间有没有办法让Java 5编译器“忽略”我代码的某些部分?

我不想简单地评论/取消注释我的部分代码,具体取决于我的java编译器的版本。

14 个答案:

答案 0 :(得分:5)

关于使用自定义类加载器和动态注释代码的建议有点不可思议,当涉及到维护和保持理智时,无论哪个可怜的灵魂在你洗牌到新牧场之后接受项目。

解决方案很简单。将受影响的类拉出到两个独立的独立项目中 - 确保包名称相同,然后编译成可以在主项目中使用的jar。如果保持包名称相同,并且方法签名相同,则没有问题 - 只需将您需要的jar版本放入部署脚本中即可。我假设您在同一个脚本中运行单独的构建脚本或具有单独的目标 - ant和maven都可以轻松处理有条件地抓取文件并复制它们。

答案 1 :(得分:4)

我认为这里最好的方法可能是使用构建脚本。您可以将所有代码放在一个位置,通过选择要包含的文件以及不包含哪些文件,您可以选择要编译的代码版本。请注意,如果您需要比每个文件更精细的控制,这可能没有用。

答案 2 :(得分:4)

假设这些类具有类似的功能,1.5和6.0的实现差异,您可以将它们合并到一个类中。然后,在不编辑注释/取消注释的源的情况下,您可以依赖编译器始终执行的优化。如果if表达式始终为false,则if语句中的代码将不包含在编译中。

您可以在其中一个类中创建一个静态变量,以确定要运行的版本:

public static final boolean COMPILED_IN_JAVA_6 = false;

然后让受影响的类检查该静态变量,并将不同的代码段放在一个简单的if语句

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

然后,当您想要编译其他版本时,您只需更改该一个变量并重新编译即可。它可能会使java文件变大,但它会整合您的代码并消除您拥有的任何代码重复。你的编辑可能会抱怨无法访问的代码或其他什么,但编译器应该幸福地忽略它。

答案 3 :(得分:3)

你可以重构你的代码,这样就不需要条件编译,只需要条件类加载。像这样:

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

}

这可能需要付出很多努力,具体取决于您拥有多少版本特定的代码。

答案 4 :(得分:2)

保留在JDK 5下构建的一个“主”源根。添加必须在JDK 6或更高版本下构建的第二个并行源根。 (应该没有重叠,即两者都不存在类。)使用接口来定义两者之间的入口点,以及一点点反射。

例如:

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

您可以在IDE中使用不同的JDK等将它们配置为单独的项目。关键是主根可以独立编译,并且非常清楚您可以在哪个根中使用,而如果您尝试编译不同的单个根的一部分分别很容易将JDK 6的使用“泄漏”到错误的文件中。

不是像这样使用Class.forName,你也可以使用某种服务注册系统 - java.util.ServiceLoader(如果main可以使用JDK 6,你需要JDK 7的可选支持!),NetBeans Lookup,Spring等等。

可以使用相同的技术来创建对可选库的支持,而不是新的JDK。

答案 5 :(得分:1)

不是真的,但有一些解决方法。看到 http://forums.sun.com/thread.jspa?threadID=154106&messageID=447625

也就是说,你应该坚持至少拥有一个用于Java 5的文件版本和一个用于Java 6的文件版本,并通过构建或适当的方式包含它们。将它全部放在一个大文件中并尝试让编译器为5来忽略它不理解的东西并不是一个好的解决方案。

HTH

- nikki -

答案 6 :(得分:1)

这将使所有Java纯粹主义者感到畏缩(这很有趣,嘿嘿)但我会使用C预处理器,将#ifdefs放在我的源代码中。 makefile,rakefile或任何控件构建都必须运行cpp来生成一个临时文件来提供编译器。我不知道蚂蚁是否可以做到这一点。

虽然stackoverflow看起来像是 所有答案的地方,但是你可能没有人会因为Java智慧而对http://www.javaranch.com感到满意。我想这个问题已经在很久以前就已经处理好了。

答案 7 :(得分:1)

这取决于您要使用的Java 6功能。对于像向JTables添加行分类器这样的简单操作,您实际上可以在运行时进行测试:

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
}

此代码必须使用Java 6编译,但可以使用任何版本运行(不引用新类)。

编辑:更正确一点,它适用于1.3之后的任何版本(根据this page)。

答案 8 :(得分:1)

您可以专门在Java6上进行所有编译,然后使用System.getProperty(“java.version”)有条件地运行Java5或Java6代码路径。

只要不执行Java6代码路径,您就可以在类中使用仅Java6代码,并且该类在Java5上运行正常。

这是一个技巧,用于编写将在古老的MSJVM上运行的applet,一直到全新的Java Plug-in JVM。

答案 9 :(得分:0)

Java中没有预编译器。因此,没办法像C一样做#ifdef。 构建脚本是最好的方法。

答案 10 :(得分:0)

你可以得到条件编译,但不是很好 - javac会忽略无法访问的代码。因此,如果您正确构建了代码,则可以让编译器忽略部分代码。要正确使用它,您还需要将正确的参数传递给javac,以便它不会将无法访问的代码报告为错误,并拒绝编译: - )

答案 11 :(得分:0)

上面提到的公共静态最终解决方案还有一个额外的好处,作者没有提到 - 据我所知,编译器将在编译时识别它并编译出if语句中引用的任何代码最终变量。

所以我认为这是您正在寻找的确切解决方案。

答案 12 :(得分:0)

一个简单的解决方案可能是:

  • 将不同的类放在正常的类路径之外。
  • 编写一个简单的自定义类加载器,并将其作为默认值安装在main中。
  • 对于除5/6之外的所有类,cassloader可以推迟到其父级(普通系统类加载器)
  • 对于5/6个(这应该是父母无法找到的唯一的),它可以通过'os.name'属性或您自己的属性决定使用哪个。

答案 13 :(得分:0)

您可以使用反射API。将所有1.5代码放在一个类中,将1.6 api放在另一个类中。在你的ant脚本中创建两个目标,一个用于1.5,不能编译1.6类,另一个用于1.6,不能编译1.5的类。在你的代码中检查你的java版本并使用反射加载适当的类,javac不会抱怨缺少函数。这就是我如何在Windows上编译我的MRJ(Mac Runtime for Java)应用程序。