Conditional Debugging without #ifdef

Problem

You want conditional compilation and Java doesn’t seem to provide it.

Solution

Use constants or command-line arguments, depending upon the goal.

Discussion

Some older languages such as C, PL/I, and C++ provide a feature known as conditional compilation. Conditional compilation means that parts of the program can be included or excluded at compile time based upon some condition. One thing it’s often used for is to include or exclude debugging print statements. When the program appears to be working, the developer is struck by a fit of hubris and removes all the error checking :-). A more common rationale is that the developer wants to make the finished program smaller -- a worthy goal -- or run faster by removing conditional statements.

Although Java lacks any explicit conditional compilation, there is a kind of conditional compilation implicit in the language. All Java compilers must do flow analysis to ensure that all paths to a local variable’s usage pass through a statement that assigns it a value first, that all returns from a function pass out via someplace that provides a return value, and so on. Imagine what the compiler will do when it finds an if statement whose value is known to be false at compile time. Why should it even generate code for the condition? True, you say, but how can the results of an if statement be known at compile time? Simple: through final boolean variables. Further, if the value of the if condition is known to be false, then the body of the if statement should not be emitted by the compiler either. Presto -- instant conditional compilation!

// IfDef.java
final boolean DEBUG = false;
System.out.println("Hello, World ");
if (DEBUG) {
        System.out.println("Life is a voyage, not a destination");
}

Compilation of this program and examination of the resulting class file reveals that the string “Hello” does appear, but the conditionally printed epigram does not. The entire println has been omitted from the class file. So Java does have its own conditional compilation mechanism.

darian$ jr IfDef
 jikes +E  IfDef.java
 java IfDef
Hello, World
darian$ strings IfDef.class | grep Life # not found!
darian$ javac IfDef.java # try another compiler
darian$ strings IfDef.class | grep Life # still not found!
darian$

What if we want to use debugging code similar to this, but have the condition applied at runtime? We can use System.properties (Section 2.3) to fetch a variable. Section 1.12 uses my Debug class as example of a class whose entire behavior is controlled this way.

But this is as good a place as any to interject about another feature, inline code generation. The C world has a language keyword _ _inline, which is a hint to the compiler that the function (method) is not needed outside the current source file. Therefore, when the C compiler is generating machine code, a call to the _ _inline function can be replaced by the actual method body, eliminating the overhead of pushing arguments onto a stack, passing control, retrieving parameters, and returning values. In Java, making a method final enables the compiler to know that it can be inlined, or emitted in line. This is an optional optimization that the compiler is not obliged to perform, but may for efficiency.

Get Java Cookbook now with the O’Reilly learning platform.

O’Reilly members experience books, live events, courses curated by job role, and more from O’Reilly and nearly 200 top publishers.