我的项目需要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
...
}
打开文件,如双击(start
Windows命令或open
Mac OS X命令等效)。由于无法使用Java 1.5进行编译,我想在编译期间将其排除,并替换为使用run32dll
为Windows调用open
或为Mac OS X调用Runtime.exec
的另一种方法。
问题:我该怎么办?注释可以帮到这里吗?
注意:我使用ant,我可以创建两个java文件OS4J5.java
和OS4J6.java
,它们将包含带有Java 1.5和1.6所需代码的OS
类,并在编译之前将其中一个复制到OS.java
(或者一种丑陋的方式 - 替换内容OS.java
有条件地取决于java版本)但我不想这样做,如果有另一种方式。
详细说明:在C中我可以使用ifdef, ifndef
,在Python中没有编译,我可以使用hasattr
或其他东西检查一个功能,在Common Lisp中我可以使用#+feature
。 Java有类似的东西吗?
找到this post但它似乎没有帮助。
任何帮助是极大的赞赏。 KH。
不,Java中没有任何对条件编译的支持。
通常的计划是将应用程序的操作系统特定位隐藏在Interface
后面,然后在运行时检测操作系统类型并使用Class.forName(String)
加载实现。
在你的情况下没有理由你不能使用Java 1.6与OS*
编译两个-source 1.5 -target 1.5
(以及你的整个应用程序)然后在工厂方法中获取OS
类(现在将是一个接口)检测到java.awt.Desktop
class是可用的并加载正确的版本。
就像是:
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();
}
}
}
隐藏像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
,/*{{
块内的/*}}
。
下面介绍的Ant脚本提供了很好的清洁技巧。
链接:https://weblogs.java.net/blog/schaefa/archive/2005/01/how_to_do_condi.html
在例如,
//[ifdef]
public byte[] getBytes(String parameterName)
throws SQLException {
...
}
//[enddef]
用Ant脚本
<filterset begintoken="//[" endtoken="]">
<filter token="ifdef" value="${ifdef.token}"/>
<filter token="enddef" value="${enddef.token}"/>
</filterset>
请转到上面的链接了解更多详情。
您可以使用反射进行调用,并使用Java 5编译代码。
EG
Class clazz = Class.forName("java.package.ClassNotFoundInJavav5");
Method method = clazz.getMethod("methodNotFoundInJava5", Class1.class);
method.invoke(args1);
您可以捕获任何异常并回退到适用于Java 5的内容。
我不是一个优秀的Java专家,但似乎支持Java中的条件编译并且很容易。请阅读:
http://www.javapractices.com/topic/TopicAction.do?Id=64
引用要点:
条件编译实践用于可选地从类的编译版本中删除代码块。它使用编译器将忽略任何无法访问的代码分支的事实。要实现条件编译,
- 将静态最终布尔值定义为某个类的非私有成员
- 在if块中有条件地编译的地方代码,用于计算布尔值
- 将boolean的值设置为false以使编译器忽略if块;否则,保持其值为真
当然,这让我们可以在任何方法中“编译”代码块。要删除类成员,方法甚至整个类(可能只留下存根),您仍然需要预处理器。
在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中它会发现并使用更新版本的类。
如果你不想在你的应用程序中使用有条件的代码块,那么预处理器只是方法,你可以看看java-comment-preprocessor,它可以用于maven和ant项目 附: 我也做了some example how to use preprocessing with Maven to build JEP-238 multi-version JAR without duplication of sources
Java Primitive Specializations Generator支持条件编译:
/* if Windows compilingFor */
start();
/* elif Mac compilingFor */
open();
/* endif */
此工具具有Maven和Gradle插件。