我维护一个Java Swing应用程序。
为了向后兼容java 5(对于Apple机器),我们维护了两个代码库,一个使用Java 6中的功能,另一个没有这些功能。
除了使用Java 6功能的3-4个类之外,代码大致相同。
我希望只保留1个代码库。 在编译期间有没有办法让Java 5编译器“忽略”我代码的某些部分?
我不希望简单地评论/取消注释我的代码部分,具体取决于我的java编译器的版本。
我维护一个Java Swing应用程序。
为了向后兼容java 5(对于Apple机器),我们维护了两个代码库,一个使用Java 6中的功能,另一个没有这些功能。
除了使用Java 6功能的3-4个类之外,代码大致相同。
我希望只保留1个代码库。 在编译期间有没有办法让Java 5编译器“忽略”我代码的某些部分?
我不希望简单地评论/取消注释我的代码部分,具体取决于我的java编译器的版本。
关于使用自定义类加载器和动态注释代码的建议有点不可思议,当涉及到维护和保持灵性时,无论哪个可怜的灵魂在你洗牌到新的牧场后接受项目。
解决方案很简单。 将受影响的类拉出到两个独立的独立项目中 - 确保包名称相同,然后编译成可以在主项目中使用的jar。 如果保持包名称相同,并且方法签名相同,则没有问题 - 只需将您需要的jar版本放入部署脚本中即可。 我假设您在同一个脚本中运行单独的构建脚本或具有单独的目标 - ant和maven都可以轻松处理有条件地抓取文件并复制它们。
我认为这里最好的方法可能是使用构建脚本。 您可以将所有代码放在一个位置,通过选择要包含的文件以及不包含哪些文件,您可以选择要编译的代码版本。 请注意,如果您需要比每个文件更精细的控制,这可能没有用。
假设类具有类似的功能,实现中的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文件变大,但它会整合您的代码并消除您拥有的任何代码重复。 您的编辑可能会抱怨无法访问的代码或其他什么,但编译器应该幸福地忽略它。
您可以重构代码,这样就不需要条件编译,只需要条件类加载。 像这样的东西:
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();
}
}
}
}
这可能需要付出很多努力,具体取决于您拥有多少版本特定的代码。
保留在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中的单独项目。关键是主根可以独立编译,并且非常清楚您可以在哪个根中使用,而如果您尝试编译的不同部分单独的根分开很容易将JDK 6的使用“泄漏”到错误的文件中。
您可以使用某种服务注册系统 - java.util.ServiceLoader(如果main可以使用JDK 6,并且您需要对JDK 7的可选支持!),NetBeans Lookup,Spring等,而不是像这样使用Class.forName。等等
可以使用相同的技术来创建对可选库的支持,而不是新的JDK。
不是真的,但有一些解决方法。 请参阅http://forums.sun.com/thread.jspa?threadID=154106&messageID=447625
也就是说,你应该坚持至少拥有一个用于Java 5的文件版本和一个用于Java 6的文件版本,并通过构建或适当的方式包含它们。 将它全部放在一个大文件中并尝试让编译器为5来忽略它不理解的东西并不是一个好的解决方案。
HTH
- nikki -
这将使所有Java纯粹主义者感到畏缩(这很有趣,嘿嘿)但我会使用C预处理器,将#ifdefs放在我的源代码中。 makefile,rakefile或任何控件构建都必须运行cpp来生成一个临时文件来提供编译器。 我不知道蚂蚁是否可以做到这一点。
虽然stackoverflow看起来像是所有答案的地方,但你可能没有人看到http://www.javaranch.com的Java智慧。 我想这个问题已经在那里得到了解决,很久以前。
这取决于您要使用的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以来的任何版本(根据此页面 )。
您可以在Java6上完成所有编译,然后使用System.getProperty(“java.version”)有条件地运行Java5或Java6代码路径。
您可以在类中使用仅Java6代码,只要不执行仅Java6代码路径,该类就可以在Java5上正常运行。
这是一个技巧,用于编写将在古老的MSJVM上运行的applet,直到全新的Java Plug-in JVM。
您可以使用反射API。 将所有1.5代码放在一个类中,将1.6 api放在另一个类中。 在你的ant脚本中创建两个目标,一个用于1.5,不能编译1.6类,另一个用于1.6,不能编译1.5的类。 在你的代码中检查你的java版本并使用反射加载适当的类,javac不会抱怨缺少函数。 这就是我如何在Windows上编译我的MRJ(Mac Runtime for Java)应用程序。
Java中没有预编译器。 因此,无法在C中执行#ifdef。构建脚本将是最好的方法。
你可以得到条件编译,但不是很好 - javac将忽略无法访问的代码。 因此,如果您正确构建了代码,则可以让编译器忽略部分代码。 要正确使用它,您还需要将正确的参数传递给javac,以便它不会将无法访问的代码报告为错误,并拒绝编译:-)
上面提到的公共静态最终解决方案还有一个额外的好处,作者没有提到 - 据我所知,编译器将在编译时识别它并编译出引用该最终变量的if语句中的任何代码。
所以我认为这是您正在寻找的确切解决方案。
一个简单的解决方案是: