13

Is it possible in Java to do a sort of #ifdef thing, like in C/C++?

Example:

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
        }
    }
}

Note that even if ANDROID is false, as in the example, it will still try to compile the code inside of the if, even though it won't (and shouldn't) compile.

I'm looking for a way to do conditional compilation -- the compiler shouldn't even look at the if if ANDROID is false.

The context of my question is that I have a Processing application in Eclipse. I'm using both normal Processing and Processing for Android in two separate projects, but I want to be able to move the source code of the projects between one another without having compiler errors. For example, I want to be able to have source code files that I can move from the Android project to the desktop project and only have to change a couple of things -- for example, changing ANDROID = true to ANDROID = false.

I really need it to be conditional compilation because when I copy the source code from the Android project to the desktop project, the desktop libraries obviously won't include Android libraries, and then the source code won't even compile.

EDIT: So now that I know that there is no preprocessor in Java, my question is: is there any other way to have this functionality in my projects (being able to copy source code from one to the other with only very minor changes) without having to manually [un]comment specific pieces of code and having to remember where those are?

EDIT 2: This is not a duplicate of the other question because my question includes code that may have compiler errors in it, whereas the question that this was closed as a duplicate of does not. (That other question concerns only code that would compile fine even without #ifdefs.) To explain, the most highly rated (and accepted) answer for the other question talks about code that is compiled, but is simply not emitted in the bytecode. However, my question concerns code that would not even compile originally.

Jashaszun
  • 9,207
  • 3
  • 29
  • 57
  • the preprocessor does not exists in java – Blackbelt Oct 30 '13 at 13:53
  • I updated my answer for your edit. – Sietse van der Molen Oct 30 '13 at 18:54
  • Have you looked at the duplicate question? Are you having actual problems compiling this code, or are you assuming you will? The answers in the other question seem to contradict what you're saying. – Collin Feb 23 '14 at 16:31
  • Could you edit to give a specific example of why the code wouldn't compile? Are you talking only about imports that might not be available when compiling? – Dan Getz Jul 29 '15 at 21:29
  • @DanGetz Yes, I'm talking about imports (and thus function calls/class instantiations, etc.) that should not be looked at by the compiler when the compilation "symbol" is set. As I said in the answer, I really need it to be conditional compilation because when I copy the source code from the Android project to the desktop project, the desktop libraries won't include Android libraries, and then the source code won't even compile (because stuff is getting referenced that doesn't exist). – Jashaszun Jul 29 '15 at 21:32
  • @Jashaszun Both projects are compiled on the desktop, so it ought to be possible to compile the desktop program, with whatever Android jars are used in compiling the Android version in the classpath. This should let you use simple `if`s around your code, and compile both for Android and non-Android. (You won't get compile-time errors or warnings if you mistakenly leave Android calls in the desktop version, though.) – Dan Getz Jul 29 '15 at 21:40
  • Java is designed to be 'written once and run anywhere'. What you're trying to do is have a platform-specific version of a program. Either they need to be compiled separately or include the core libraries of the other program. This may suggest a design issue you have: have you considered writing your primary business code as a library to be consumed by two separate programs utilizing it, rather than necessarily sitting atop all possible library scenarios? – Nathaniel Ford Jul 29 '15 at 21:41
  • @NathanielFord Well, I was trying to write a game, and actually a huge part of the code is the graphics (which depends on whether I'm using Android or simply Processing). I don't think it would be feasible to separate it out and abstract it from the graphics libraries. – Jashaszun Jul 29 '15 at 21:44
  • Consider the `Injection` design pattern, then: as long as you normalize the interface to your graphics engines you can manage that. – Nathaniel Ford Jul 29 '15 at 21:45

6 Answers6

3

As Java does not natively include a preprocessor, it would be incumbent upon you to manually execute one before compiling. The c preprocessor is m4, which you can run yourself.

Nathaniel Ford
  • 20,545
  • 20
  • 91
  • 102
nobody
  • 39
  • 1
  • how would I do this on Windows? – Jashaszun Oct 30 '13 at 13:55
  • 1
    This is actually the "correct" answer. If you really wanted a preprocessr, the easiest approach would be 1) load a C/C++ toolset (like GCC), 2) write an "Ant" build.xml that runs the preprocessor before it compiles, 3) use the Ant script for all your builds. – paulsm4 Oct 30 '13 at 13:57
  • @paulsm4 how do you use Ant? – Jashaszun Oct 30 '13 at 14:00
  • 2
    I suppose `m4` is very different from `C` preprocessor! – Gyro Gearless Oct 30 '13 at 14:02
  • cpp is the standard C pre-processor on IXish systems: -1 – alk Oct 30 '13 at 18:23
  • For some historical context here: the C preprocessor _used to be_ commonly implemented with M4, or a wrapper around M4. As the preprocessor rules grew in complexity, implementations of C tools moved from being in other languages, and became written in C themselves. – Ti Strga Dec 21 '18 at 17:59
3

As others have said, the answer to your actual question is no.

However, you might approach your problem by isolating the Android or desktop code. You could do this by having three separate projects in eclipse:

  • Core: This is the "shared" code that exists between both versions.
  • Android: This contains only the code that runs on Android.
  • Desktop: This contains only the code that runs on desktop.

Both your Android and Desktop projects would contain the Core project on their classpaths. In eclipse, you'd do this by going to your Java Build Path, then clicking the Projects tab, then adding the Core project to the "Required projects" list.

Then you'd set your code up so your Android and Desktop projects are what you actually deploy, and your Core project contains the code shared between them. Here's a simple example. Let's say we have an example class that looks like this:

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)
      }
   }
}

You could get around this by refactoring your code to isolate the "core" code that will work on both desktop and Android. Something like this:

//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);
}

Then, you'd isolate the code that will only work on Desktop:

//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);
   }
}

And the code that will only work on 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);
   }
}

Note that this is just an example, and I know that both System.out.println() and Log.v() could work on either platform. But the idea is the same: split your project up into multiple projects, and use interfaces to abstract away the behavior that changes between platforms.

Kevin Workman
  • 41,537
  • 9
  • 68
  • 107
1

There are no-preprocessors in java like C,C++ etc. All you can do is comment out the code.

Anup Cowkur
  • 20,443
  • 6
  • 51
  • 84
  • 5
    Noone keeps the OP from running a C pre-processor against his/her Java sources. – alk Oct 30 '13 at 18:40
0

No, there is no such thing as a preprocessor in Java that can hide chunks of code to the JVM.

EDIT: While you could of course run any program against your code base to preprocess it, think about if you really want this. The Android code will diverge more from the other Java code in time and your code will be littered with those #ifdef-like statements. Your IDE will also still see them and give you errors in both areas of code. In this case it's much easier to just make two projects out of it or, and that's my advice, create a platform independent library which you include in both projects and includes the functionality you need.

  • Noone keeps the OP from running a C pre-processor against his/her Java sources. – alk Oct 30 '13 at 18:39
  • Of course, just like no one keeps him from deleting/adding the pieces of code by hand or magnetized needles, but is it really a sane thing to do? – Sietse van der Molen Oct 30 '13 at 18:42
  • 1
    Sure, as far as I know `#` is not valid Java. So misunderstandings are not expected. However, I've seen projects doing exactly this. Remember, that the C pre-processor isn't related to C in any way. It's just some sort of text file filter. Btw: Doing the pre-prcoessor's work by hand is really a hard job. That's why it exists. For the needles, that a different story though .. ;-) – alk Oct 30 '13 at 18:46
0

Use #ifdef and friends as in C and run the Java sources through the C pre-processor before compiling them.

For gcc the pre-processor is called cpp, for VC it's cl.exe using the option /P.

alk
  • 69,737
  • 10
  • 105
  • 255
0

By defining productFlavors in build you can use folders that will be compiled when specific flavor is chosen thus you can make code in same codebase available conditionally at compile time.

Renetik
  • 5,887
  • 1
  • 47
  • 66