我维护一个Java Swing应用程序。
为了向后兼容java 5(对于Apple机器),我们维护了两个代码库,一个使用Java 6中的功能,另一个没有这些功能。
除了使用Java 6功能的3-4个类之外,代码大致相同。
我希望维护1个代码库。在编译期间有没有办法让Java 5编译器“忽略”我代码的某些部分?
我不想简单地评论/取消注释我的部分代码,具体取决于我的java编译器的版本。
答案 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)
一个简单的解决方案可能是:
答案 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)应用程序。