允许非编译代码的 #ifdef 的 Java 等效方式

13
在Java中是否存在类似于C/C++中的#ifdef这样的东西?
例如:
class Test
{
    public static final boolean ANDROID = false;

    public Test()
    {
        if (ANDROID)
        {
            // do stuff that won't compile if not on android
        }
        else
        {
            // do stuff that should be only done on desktop
        }
    }
}

请注意,即使像示例中那样ANDROIDfalse,它仍然会尝试编译if内的代码,尽管它不会(也不应该)编译。
我正在寻找一种条件编译的方法--如果ANDROIDfalse,编译器甚至不应该查看if
我的问题背景是,我在Eclipse中有一个Processing应用程序。我在两个独立的项目中同时使用普通Processing和处理Android,但我希望能够在这两者之间移动项目的源代码而不必担心编译器错误。例如,我希望能够有源代码文件,可以从Android项目移动到桌面项目,并只需要改变几个东西--例如,将ANDROID = true更改为ANDROID = false
我真的需要条件编译,因为当我将源代码从安卓项目复制到桌面项目时,桌面库显然不包含安卓库,然后源代码甚至无法编译。
编辑:现在我知道Java中没有预处理器,我的问题是:是否有其他方式在我的项目中具有此功能(能够将源代码从一个项目复制到另一个项目,并仅需进行非常小的更改),而不必手动注释特定的代码片段并记住它们的位置?
编辑2:这不是另一个问题的重复,因为我的问题包括可能有编译错误的代码,而被关闭的问题则没有。那个问题只涉及即使没有#ifdef也可以编译的代码。最受欢迎的(和被接受的)答案解释了一种已经编译但未在字节码中呈现的代码。但是,我的问题涉及原本无法编译的代码。

预处理器在Java中不存在。 - Blackbelt
我已经针对你的编辑更新了我的答案。 - Sietse van der Molen
你看过重复的问题吗?你是真的遇到编译这段代码的问题,还是只是假设会有问题?其他问题中的答案似乎与你所说的相矛盾。 - Collin
你能否编辑一下,给出一个具体的例子说明为什么代码无法编译?你是只在谈论编译时可能不可用的导入吗? - Dan Getz
@DanGetz 是的,我在谈论那些在编译“符号”设置时不应被编译器查看的导入(因此也包括函数调用/类实例化等)。正如我在答案中所说,我真的需要它是条件编译,因为当我将源代码从Android项目复制到桌面项目时,桌面库将不包括Android库,然后源代码甚至无法编译(因为引用了不存在的内容)。 - Jashaszun
显示剩余4条评论
6个回答

3

由于Java本身不包含预处理器,所以在编译之前需要您手动执行一个预处理器。C预处理器是m4,您可以自己运行它。


我该如何在Windows上完成这个任务? - Jashaszun
1
这实际上是“正确”的答案。如果你真的想要一个预处理器,最简单的方法是:1)加载C/C++工具集(如GCC),2)编写一个在编译之前运行预处理器的“Ant” build.xml文件,3)将Ant脚本用于所有构建。 - paulsm4
@paulsm4 你如何使用Ant? - Jashaszun
2
我想m4C预处理器非常不同! - Gyro Gearless
cpp是IXish系统上的标准C预处理器:-1。 - alk
这里需要一些历史背景:C预处理器曾经常用M4或M4的包装器实现。随着预处理器规则变得越来越复杂,C工具的实现从其他语言转向了C本身编写。 - Ti Strga

3
正如其他人所说,对于你实际问题的答案是否定的。
但是,你可以通过隔离Android或桌面代码来解决问题。您可以通过在eclipse中拥有三个单独的项目来实现此目的:
核心(Core):这是两个版本之间存在的“共享”代码。
Android:仅包含在Android上运行的代码。
桌面(Desktop):仅包含在桌面上运行的代码。
您的Android和桌面项目都将包含Core项目在其类路径中。在eclipse中,您可以通过转到Java Build Path,然后单击“Projects”选项卡,然后将Core项目添加到“Required projects”列表中来完成此操作。
然后,您需要设置代码,使Android和桌面项目成为您实际部署的项目,而Core项目包含它们之间共享的代码。以下是一个简单的例子。假设我们有一个示例类,看起来像这样:
public class Adder{
   public void addAndPrint(int x, int y){

      //code that will work on both Android and desktop
      int sum = x+y;

      if (ANDROID){
         //code that will only work on Android
         Log.v("example", "Sum:" + sum);
      }
      else{
         //code that will only work on desktop
         System.out.println("Sum: " + sum)
      }
   }
}

您可以通过重构代码来隔离适用于桌面和Android平台的“核心”代码。类似这样:

//example core class
public class CoreAdder{

   Printer printer;

   public CoreAdder(Printer printer){
      this.printer = printer;
   }

   public void addAndPrint(int x, int y){
      int sum = x+y;
      printer.print("Sum: " + sum);
   }
}

//example core interface. We might print differently on
//Android and Desktop, so implement this interface in each.
public interface Printer{
   public void print(String printMe);
}

然后,您需要将仅适用于桌面端的代码隔离出来:

//on desktop, use System.out.println()
public class DesktopPrinter implements Printer{
   public void print(String printMe){
      System.out.println(printMe);
   }
}

//on desktop, entry point is main()
public class DesktopMain{
   public static void main(String... args){
      DesktopPrinter printer = new DesktopPrinter();
      CoreAdder adder = new CoreAdder(printer);
      adder.addAndPrint(1, 2);
   }
}

仅适用于Android的代码:

//on Android, use a logger
public class AndroidPrinter implements Printer{
   public void print(String printMe){
      Log.v("example", "index=" + i);
   }
}

//on Android, entry point is Activity
public class AndroidActivity extends Activity{

   public void onCreate(Bundle savedInstanceState) {
      AndroidPrinter printer = new AndroidPrinter ();
      CoreAdder adder = new CoreAdder(printer);
      adder.addAndPrint(1, 2);
   }
}

注意,这只是一个例子,我知道System.out.println()Log.v()都可以在任何平台上工作。但理念是一样的:将你的项目拆分成多个项目,并使用接口来抽象出在不同平台之间变化的行为。

1

Java中没有像C、C++等语言中的预处理器。你所能做的只是将代码注释掉。


5
没有人阻止OP对他/她的Java源代码运行C预处理器。 - alk

0

Java中没有预处理器可以隐藏JVM的代码块。

编辑: 当然,您可以运行任何程序来对您的代码进行预处理,但请考虑是否真的需要这样做。Android代码将会与其他Java代码越来越不同,您的代码将会被那些类似于#ifdef的语句所淹没。您的IDE也会看到它们,并在代码的两个区域中给出错误。在这种情况下,最好将其分成两个项目或者(这是我的建议)创建一个平台无关的库,您可以将其包含在两个项目中,以包含您需要的功能。


没有人阻止OP对他/她的Java源代码运行C预处理器。 - alk
当然,就像没有人阻止他手动删除/添加代码片段或磁化针一样,但这真的是明智之举吗? - Sietse van der Molen
1
据我所知,#<some pre-processor directive>不是有效的Java代码。因此,不应该会有误解。然而,我见过一些项目确实这样做了。请记住,C预处理器与C没有任何关系,它只是一种文本文件过滤器。顺便说一句:手动完成预处理器的工作真的很难,这也是为什么它存在的原因。但对于那些需要针对性操作的情况,就另当别论了...;-) - alk

0

在编译 Java 源代码之前,像 C 语言一样使用 #ifdef 等指令,并通过 C 预处理器运行。

对于 gcc 编译器,预处理器称为 cpp;对于 VC 编译器,可以使用选项 /P 来调用 cl.exe。


在编辑代码时,这会在Eclipse中产生很多错误,因为它仍然看到两个版本。 - Sietse van der Molen
没有合适的附加组件,这看起来很不美观。 - alk

0
通过在构建中定义 productFlavors,您可以使用文件夹,在选择特定风味时编译,因此您可以使同一代码库中的代码在编译时有条件地可用。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接