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

时间:2020-03-05 18:56:27  来源:igfitidea点击:

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

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

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

我只想维护1个代码库。编译期间是否有办法让Java 5编译器"忽略"我的代码的某些部分?

我不希望仅根据我的Java编译器的版本来注释/取消注释代码的某些部分。

解决方案

回答

我认为这里最好的方法可能是使用构建脚本。我们可以将所有代码放在一个位置,并通过选择要包含的文件和不包含的文件,可以选择要编译的代码版本。请注意,如果我们需要比每个文件更细粒度的控制,这可能无济于事。

回答

并非如此,但是有解决方法。看
http://forums.sun.com/thread.jspa?threadID=154106&messageID=447625

也就是说,我们应该坚持至少拥有一个Java 5的文件版本和Java 6的一个文件版本,并通过build或者make适当地包含它们。将所有内容都粘贴在一个大文件中并尝试让5的编译器忽略它不了解的内容不是一个好的解决方案。

高温超导

-nikki-

回答

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

虽然stackoverflow似乎将是所有答案的地方,但我们可以期待Java的智慧再到http://www.javaranch.com。我想这个问题早在很久以前就已经解决了。

回答

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

回答

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

回答

保留一个在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);
    }
}
---%<---

我们可以使用不同的JDK等在IDE中将它们配置为单独的项目。关键是主根可以独立编译,并且很清楚可以在哪个根中使用什么,而如果我们尝试编译a的不同部分,单独使用单个根目录很容易意外地将JDK 6的使用"泄漏"到错误的文件中。

除了使用这样的Class.forName之外,我们还可以使用某种服务注册系统java.util.ServiceLoader(如果main可以使用JDK 6,并且我们想要对JDK 7的可选支持!),NetBeans Lookup,Spring等。 。

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

回答

我们可能可以重构代码,以便确实不需要条件编译,只需条件类加载即可。像这样的东西:

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

}

根据我们拥有多少个特定于版本的代码段,这可能会花费很多精力。

回答

假设这些类具有相似的功能,但在实现方式上有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文件更大,但将合并代码并消除我们所拥有的任何代码重复。编辑器可能会抱怨无法访问的代码或者其他任何东西,但编译器应该很乐意忽略它。

回答

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

因此,我认为这是我们正在寻找的确切解决方案。

回答

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

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

回答

这取决于要使用的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(根据此页)以来的任何版本一起使用。

回答

有关使用自定义类加载器和动态注释的代码的建议在维护和保留理智的问题上有些不合理,无论我们是精疲力尽的灵魂在改组新牧场后还是选择了该项目。

解决方案很简单。将受影响的类拉入两个独立的独立项目中,以确保程序包名称相同,然后将其编译为jar,然后可以在主项目中使用。如果我们保持程序包名称相同,并且方法签名相同,那么只要将所需的jar版本放到部署脚本中,就不会出现问题。我假设我们在同一脚本中运行单独的构建脚本或者具有单独的目标ant,而maven可以轻松地处理有条件的抓取文件并进行复制。

回答

我们可以完全在Java6上进行所有编译,然后使用System.getProperty(" java.version")有条件地运行Java5或者Java6代码路径。

我们可以在一个类中包含仅Java6的代码,并且只要不执行仅Java6的代码路径,该类就可以在Java5上正常运行。

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

回答

我们可以使用反射API。将我们所有的1.5代码放在一个类中,将1.6 API放在另一个类中。在ant脚本中,创建两个目标,一个目标为1.5,该目标将不会编译1.6类;另一个目标为1.6,该目标将不会编译1.5类。在代码中检查Java版本并使用反射方式加载适当的类,以使javac不会抱怨缺少函数。这就是我可以在Windows上编译MRJ(适用于Java的Mac运行时)应用程序的方式。