Java条件编译:如何防止代码块被编译?
Posted
技术标签:
【中文标题】Java条件编译:如何防止代码块被编译?【英文标题】:Java conditional compilation: how to prevent code chunks from being compiled? 【发布时间】:2011-05-30 09:26:24 【问题描述】:我的项目需要 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 编译,我想在编译期间将其排除并替换为另一种方法,该方法调用 Windows 的 run32dll
或使用 Runtime.exec
的 Mac OS X 的 open
。
问题:我该怎么做?注释可以在这里提供帮助吗?
注意:我使用 ant,我可以创建两个 java 文件 OS4J5.java
和 OS4J6.java
,它们将包含 OS
类以及 Java 1.5 和 1.6 所需的代码,然后将其中一个复制到 OS.java
编译(或一种丑陋的方式 - 有条件地根据 java 版本替换 OS.java
的内容)但我不想这样做,如果有另一种方式。
详细说明:在 C 中我可以使用 ifdef, ifndef
,在 Python 中没有编译,我可以使用 hasattr
或其他东西检查功能,在 Common Lisp 中我可以使用 #+feature
。 Java有类似的东西吗?
找到this post,但似乎没有帮助。
非常感谢任何帮助。 kh。
【问题讨论】:
将OS4J5
和OS4J6
编译成.class
文件,然后编写一个自定义类加载器来根据运行时版本选择哪一个?
@JUST 我不能同时使用 Java 1.5 编译,因为它不支持 java.awt.Desktop
,而且我会在 OS4J6.java
上收到错误。
用 1.6 编译。只要您不使用仅限 1.6 的 API,.class
文件就是向后兼容的。
@JUST 我用 1.6 编译了一个简单的测试 java 文件(没有特定于 1.6 的代码),但我无法用 1.5 运行它:Exception in thread "main" java.lang.UnsupportedClassVersionError: Bad version number in .class file
。
预处理器做的比条件代码编译更多。我感到有点沮丧的是,人们将预处理器问题标记为重复:关于“条件编译”主题的主题。
【参考方案1】:
您好,当我在 Java SDK abd android 之间共享库并且在两种环境中都使用图形时,我遇到了类似的问题,所以基本上我的代码必须同时使用两者 java.awt.Graphics 和 android.graphics.Canvas, 但我不想复制几乎任何代码。 我的解决方案是使用包装器,所以我以间接方式访问 graphisc API,并且 我可以更改几个导入,以导入我想要编译项目的包装器。 这些项目有一些锥形阴影,有些是独立的,但除了几个包装器等之外,没有任何重复的东西。 我认为这是我能做的最好的。
【讨论】:
【参考方案2】:Manifold framework 中的 Java 有一个新的 Preprocessor。它是一个 javac 插件,这意味着它直接与 Java 编译器集成——无需管理构建步骤、代码生成目标等。
【讨论】:
【参考方案3】:Java Primitive Specializations Generator支持条件编译:
/* if Windows compilingFor */
start();
/* elif Mac compilingFor */
open();
/* endif */
这个工具有 Maven 和 Gradle 插件。
【讨论】:
【参考方案4】:如果您不想在应用程序中启用有条件的代码块,那么预处理器是唯一的方法,您可以查看java-comment-preprocessor,它可用于 maven 和 ant 项目 附: 我也做了some example how to use preprocessing with Maven to build JEP-238 multi-version JAR without duplication of sources
【讨论】:
【参考方案5】:在 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 中,它将找到并使用该类的更新版本。
【讨论】:
【参考方案6】:我不是那么出色的 Java 专家,但似乎 Java 中的条件编译是受支持且易于实现的。请阅读:
http://www.javapractices.com/topic/TopicAction.do?Id=64
引用要点:
条件编译实践用于选择性地从类的编译版本中删除代码块。它使用编译器将忽略任何无法访问的代码分支的事实。 实现条件编译,
将静态最终布尔值定义为某个类的非私有成员 将有条件编译的代码放置在计算布尔值的 if 块中 将布尔值设置为 false 以使编译器忽略 if 块;否则,保持其值为真
当然,这让我们可以“编译”出任何方法中的代码块。要删除类成员、方法甚至整个类(可能只留下一个存根),您仍然需要一个预处理器。
【讨论】:
只有当您有可编译的代码时,您在此处指定的方法才有效。【参考方案7】:不,Java 中不支持条件编译。
通常的计划是将应用的操作系统特定位隐藏在Interface
后面,然后在运行时检测操作系统类型并使用Class.forName(String)
加载实现。
在您的情况下,您没有理由不能使用 Java 1.6 和 -source 1.5 -target 1.5
编译 OS*
(实际上是您的整个应用程序),然后在工厂方法中获取 OS
类(其中现在将是一个接口)检测java.awt.Desktop
类可用并加载正确的版本。
类似:
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();
【讨论】:
条件编译可以半做:见***.com/a/1922636/632951【参考方案8】:下面介绍的 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>
更多详情请点击上方链接。
【讨论】:
链接已损坏。【参考方案9】:在 Gareth 提议的接口后面隐藏两个实现类可能是最好的方法。
也就是说,您可以使用 ant 构建脚本中的替换任务引入一种条件编译。诀窍是在您的代码中使用 cmets,这些 cmets 在编译源代码之前通过文本替换打开/关闭,例如:
/* 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
。
【讨论】:
是的,这是一个有用的技巧,出于安全原因,我用它在生产版本中“关闭”标准输出(即,根据运行时条件执行此操作是不够的)。【参考方案10】:您可以使用反射进行调用并使用 Java 5 编译代码。
例如
Class clazz = Class.forName("java.package.ClassNotFoundInJavav5");
Method method = clazz.getMethod("methodNotFoundInJava5", Class1.class);
method.invoke(args1);
您可以捕获任何异常并回退到适用于 Java 5 的东西。
【讨论】:
以上是关于Java条件编译:如何防止代码块被编译?的主要内容,如果未能解决你的问题,请参考以下文章