Java Conditional Debugging Without #ifdef
Problem
You want conditional compilation and Java doesn’t seem to provide it.
Solution
Use constants, command-line arguments, or assertions (Recipe 1.12), 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 make it run faster by removing
conditional statements.
Conditional compilation?
Although Java lacks any explicit conditional compilation, a kind of conditional compilation
is 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 (Recipe 2.2) to fetch a variable. Recipe 1.11 uses my Debug class as an 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/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 function marked with inline 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.
No comments:
Post a Comment