Syntax Issues

When compared to languages such as C++, Java has a very simple syntax. However, there are some points of syntax that should be covered, even for the intermediate and advanced Java programmer.

Abbreviated if

One of the things that is not well understood about the if statement is that it abbreviates evaluations in order from left to right. For example, consider the following code:

package oracle.hcj.review;
public class SyntaxIssues {
 public static void containsNested(final List list, final Object target) {
 Iterator iter = list.iterator( );
 for (Set inner = null; iter.hasNext( ); inner = (Set)iter.next( )) {
 if (inner != null) {
 if (inner.contains(target)) {
 // do code.
 }
 }
 }
 }
}


In this code, the method is passed a list of sets to determine whether the targeted element is in one of the nested sets. Since a list can contain nulls, the method wisely checks for null before dereferencing inner. As long as inner isn't null, the method checks to see whether the set contains target. This code works, but the deep nesting is not necessary. You can write the code in another way:

package oracle.hcj.review;
public class SyntaxIssues {
 public static void containsNested2(final List list, final Object target) {
 Iterator iter = list.iterator( );
 for (Set inner = null; iter.hasNext( ); inner = (Set)iter.next( )) {
 if ((inner != null) && (inner.contains(target))) {
 // do code.
 }
 }
 }
}


In this version, the method checks for null and containment on the same line. This version of the method is in no danger of throwing NullPointerExceptions because the evaluation of the if statement is abbreviated at runtime. While evaluating an if statement, the evaluations are run from left to right. Once the evaluation reaches a definitive condition that cannot be altered by any other evaluation, the remaining evaluations are skipped.

Ternary Expressions

Ternary expressions look a little strange at first. However, they can be useful in making code read better. For example, consider the following code:

package oracle.hcj.review;
public class SyntaxIssues {
 public int someMethod(final Point p) {
 if (p == null) {
 return 0;
 } else {
 return p.x + p.y;
 }
 }
}


Although this method runs properly, it is unnecessarily wordy. The same thing could have been accomplished using a much simpler ternary expression:

package oracle.hcj.review;
public class SyntaxIssues {
 public int someElegantMethod(final Point p) {
 return p == null ? 0 : p.x + p.y;
 }
}


The emphasized line is not as cryptic as it may appear at first. It is merely a large evaluation expression. If the given clause evaluates as true, then the value of the entire statement is in the ? clause. However, if the condition evaluates as false, then the value of the entire statement is in the : clause. The result of the evaluation is returned by the return statement. Ternary statements are very useful for performing evaluations such as this. However, you have to be aware of a few gotchas. For example, the following code will not work:

p == null ? System.out.println("p is null!") : return p.x + p.y;
return 0;


In this statement, the user wants to make an if-then clause that would print a message if the point passed to the method was null or return the value if the point was not null. Basically, he is going for a less wordy version of the following code:

package oracle.hcj.review;
public class SyntaxIssues {
 public static int someMethod(final Point p) {
 if (p == null) {
 System.out.println("p is null!")
 } else {
 return p.x + p.y;
 }
 }
 return 0;
}


The problem is that a ternary expression is not an if-then clause. It is an evaluation clause. Both of the clauses in the ternary expression must evaluate to something. The statement System.out.println("p is null!") evaluates to void because the return of the println( ) method is void. Additionally, the statement return p.x + p.y is pure syntactical nonsense because return is a keyword that doesn't evaluate to anything. Although ternary expressions are useful for shortening what would be an otherwise long and drawn-out piece of code, they can be cryptic to read if they are abused. I recommend that you use ternary expressions only for very small evaluations. If your evaluation is complex, you are better off going with the if-then structure.

Leveraging for Loops

The for loop is one of the most elegant and underused looping structures in the Java language. Most developers use only the basic concept of a for loop to iterate through a predefined series of numbers:

for (int idx = 0; idx < args.length; idx++) {
 // . . . do something. }


To the average reader, this code should be completely boring. However, to many developers, this is the pinnacle of for loop usage. But there are many more things that you can do with a for loop to make your code more elegant while dealing with some annoying coding issues. Although the techniques in this section are not necessarily required, they do make your code look a lot nicer. However, if there is one concept that you should take from this section, it's that for loops can do much more than simply cycle through numbers.

for loop fundamentals

One recurring problem when dealing with collections is the need to iterate through the collection and keep track of an index at the same time. For example, you may need to copy all of the x coordinates from a List of Point objects into an array of ints. The following code represents a first attempt at implementing this method:

package oracle.hcj.review;
import java.awt.Point;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
public class SyntaxIssues {
 public static int[] extractXCoords(final List points) {
 int[] results = new int[points.size( )];
 Point element = null;
 int idx = 0;
 Iterator iter = points.iterator( );
 while (iter.hasNext( )) {
 element = (Point)iter.next( );
 results[idx] = element.x;
 idx++;
 }
 return results;
 }
}


Although this piece of code will work, it isn't very elegant. The code would look much neater written in the following way:

package oracle.hcj.review;
public class SyntaxIssues {
 public static int[] extractXCoords2(final List points) {
 int[] results = new int[points.size( )];
 Point element = null;
 Iterator iter = points.iterator( );
 for (int idx = 0; iter.hasNext( ); idx++) {
 element = (Point)iter.next( );
 results[idx] = element.x;
 }
 return results;
 }
}


This second example is much more elegant than the first. However, the important thing to note about the rewritten method is that the exit clause to the for loop has nothing to do with idx. You can use the for loop structure in this manner because each of the statements inside the for loop are completely independent of each other. This is a fact that seems to escape many Java developers. The point is that the for loop has the following grammar:

for (Allocation Statement; Expression; Iteration Statement)


The allocation statement in the for loop is executed when the loop is set up. At each iteration of the loop, the expression in the middle is evaluated and, if the expression is true, the iteration statement is run. You can stick any statement or expression in the for loop regardless of whether they are using the same variables. For example, the following is perfectly legal:

package oracle.hcj.review;
public class ForLoops {
 public static void forWeird( ) {
 boolean exit = false;
 int idx = 0;
 for (System.setProperty("user.sanity", "minimal"); exit == false;
 System.out.println(System.currentTimeMillis( ))) {
 // do some code.
 idx++;
 if (idx == 10) {
 exit = true;
 }
 }
 }
}


When the loop is initialized, the system property user.sanity will be set to minimal. At each iteration of the loop, the current time in milliseconds will be printed as long as exit is false. This is a great demonstration of what you can do with for loops. Now that you know just what is possible with for loops, you can leverage them even further to do things that are a bit more mainstream.

Collection iteration with for

Iterating through collections offers you another chance to have a bit of fun with the for loop. Let's begin with typical code used to iterate through a collection of objects:

package oracle.hcj.review;
public class ForLoops {
 public static void forLong( ) {
 Properties props = System.getProperties( );
 Iterator iter = props.keySet( )
 .iterator( );
 String key = null;
 while (iter.hasNext( )) {
 key = (String)iter.next( );
 System.out.println(key + "=" + System.getProperty(key));
 }
 }
}


This snippet will iterate through all of the system properties and dump them to System.out. It works fine but it is very wordy and can be slightly problematic. For example, try the following bug on for size:

package oracle.hcj.review;
public class ForLoops {
 public static void propsDump(final Set customKeys) {
 Properties props = System.getProperties( );
 Iterator iter = props.keySet( )
 .iterator( );
 String key = null;
 System.out.println("All Properties:");
 while (iter.hasNext( )) {
 key = (String)iter.next( );
 System.out.println(key + "=" + System.getProperty(key));
 }
 System.out.println("Custom Properties:");
 iter = customKeys.iterator( );
 while (iter.hasNext( )) {
 System.out.println(key + "=" + System.getProperty(key));
 }
 }
 }
}


If you didn't see the bug in this piece of code instantly, then you are in trouble. The problem is that this code generates a logic error. In the second iteration, key = (String)iter.next( ) wasn't called. However, since key is still in scope from the previous loop, the code compiles and runs. Trivial errors such as this can consume tons of hours in complex apps. It would be great if you could scope the key variable and the iter variable to the for loop where they are used. You can do this and make your program more elegant with the following code:

package oracle.hcj.review;
public class ForLoops {
 public static void forShort( ) {
 Properties props = System.getProperties( );
 for (Iterator iter = props.keySet( ).iterator( ); iter.hasNext( );) {
 String key = (String)iter.next( );
 System.out.println(key + "=" + System.getProperty(key));
 }
 }
}


In this example, the ability of for was leveraged to isolate your iteration variable. In this example, key is defined only within the for loop, and any attempt to access it outside the for loop will generate a compiler error. Additionally, the iterator itself is scoped to the for loop, which guards against a user that forgets to set up his iterator. Finally, the increment portion of the for loop has no value at all, which is completely legal. When looking at forShort( ), you may be inclined to object that key is being allocated in a loop and that loop allocation is bad. In fact, in this example, the compiler notices that as well and optimizes out the variable to be allocated once while keeping it scoped to the for loop.

Abrupt Flow Control

Two of the more poorly understood pieces of Java syntax are the break and continue keywords. Both are used to control flow within loops or other complex statements. They can be very useful in some situations, but you need to be aware of their limitations.

break

The break keyword is probably familiar to most Java programers who have a flair for using switch statements. An example of a common usage is shown here:

package oracle.hcj.review;
public class SyntaxIssues {
 public static String decode(final int input) {
 String decoded = null;
 switch (input) {
 case 1:
 decoded = "Option 1";
 break;
 case 2:
 case 3:
 decoded = "Option 2";
 break;
 default:
 return "Option 3";
 }
 return decoded;
 }
 }
}


The break keyword tells the virtual machine to exit the switch statement. In this switch statement, if the user inputs a 1, the result will be "Option 1". If the user inputs a 2 or a 3, the result will be "Option 2". Finally, if she inputs anything else, the result will be "Option 4". The break statement lets certain cases, such as 2, fall through, but breaks out when it is done with the relevant logic. Without the break, you would always get "Option 4" as the result of the method because all lines inside the switch statement would run. The break statement is a great way to make decisions such as those in the previous example. However, every now and then you get silly people that do something like the following:

package oracle.hcj.review;
public class SyntaxIssues {
 public static String decode(final int input) {
 String decoded = null;
 switch (input) {
 case 1:
 decoded = "Option 1";
 break;
 case 2:
 case 3:
 decoded = "Option 2";
 break;
 default:
 return "Option 3";
 break; // <= someone doesn't get it!
 }
 return decoded;
 }
 }
}


In this code, the final break statement is completely unnecessary, and also unreachable. The code will exit the switch statement regardless of whether the break was there. If you find it hard to believe people actually write code like this, the templates to generate switch statements that come with Eclipse do exactly this. However, this is a rather minor, if somewhat silly, problem. Some problems with break are a lot more serious. Consider the following code:

package oracle.hcj.review;
public class SyntaxIssues {
 public static void matrixMeth(final Point[][] values) {
 for (int x = 0; x < values[0].length; x++) {
 for (int y = 0; y < values.length; y++) {
 if ((values[x][y].x < 0) || (values[x][y].y < 0)) {
 break; // exit to the System.err line.
 }
 // do something with the value
 }
 }
 System.err.println("Invalid Point in Matrix");
 // cleanup processing resources
 }
 }
}


In this piece of code, the developer wants the nested for loops to exit if a bad Point is detected in the matrix. However, the break statement doesn't do this. It merely breaks out of its current loop. The outer loop still runs, and the method processes bad data with potentially disastrous results. When you use break in your code, you should be aware of these limitations and plan accordingly. The break keyword can be extremely useful for quick and abrupt termination of complex loops but its limitations can bite you if you aren't careful.

continue

The continue keyword is similar to break in that it abruptly changes the flow control of a method. However, whereas break exits a loop completely, continue merely skips back to the loop clause itself:

package oracle.hcj.review;
public class SyntaxIssues {
 public static void continueFunc( ) {
 for (int idx = 0; idx < 1000; idx++) {
 // . . . do some complex code. if (idx == 555) {
 break;
 }
 // . . . processing code
 }
 for (int idx = 0; idx < 1000; idx++) {
 // . . . do some complex code. if (idx == 555) {
 continue;
 }
 // . . . processing code.
 }
 }
}


In the first loop, the processing code is executed only 554 times. When the value of idx is 555, the break statement is hit and the loop exits. However, in the second loop the processing code is executed 999 times. In this case, when idx is 555, the continue statement is hit, and the remainder of the logic inside the for loop is skipped. Subsequently, the loop then continues on normally with 556; therefore, the processing code is skipped only for element 555. Although this example is trivial, the use of the continue statement can be a significant asset to your development when the code inside the loop is very complex. With many such possible "skip" conditions, your code can become deeply nested. The continue statement simplifies things dramatically. Without the continue statement, you would have to enclose the entire remainder of the for loop inside an if statement.

Screenshot

The continue statement has the same semantics as the break statement in that it applies only to the loop in which the continue is enclosed.


Labels

Labels are one of those obscure pieces of Java syntax that you see only once in a blue moon. They are a way to mark a statement with an identifier to be used by a break or continue statement. The following code declares labels on two different lines in a program:

LOGLINE: System.err.println("Invalid Point in Matrix");
LINE: for (int x = 0; x < values[0].length; x++) { ... }


The label is declared by simply prefacing the line with a legal variable name and then a colon. As usual with Java, any leading whitespace on the line will be ignored. However, keep in mind that you cannot declare a label unless it is the first statement on a line. The compiler would spit out all sorts of parsing errors if you tried something like the following:

System.err.println("Invalid Point in Matrix"); LOGLINE2:


Once you have declared the label, you can use it in a break or a continue statement. The following code shows how you can use break to repair a faulty matrix:

package oracle.hcj.review;
public class SyntaxIssues {
 public static void matrixMeth2(final Point[][] values) {
 RESTART: {
 for (int x = 0; x < values[0].length; x++) {
 for (int y = 0; y < values.length; y++) {
 if ((values[x][y].x < 0) || (values[x][y].y < 0)) {
 values[x][y].x = Math.max(values[x][y].x, 0);
 values[x][y].y = Math.max(values[x][y].y, 0);
 break RESTART; // Try to process again!
 }
 // do something with the value
 }
 }
 }
 // continue processing
 }
}


In this version, if an error condition is detected, the method will try to repair the error condition and then start to process the matrix all over again. This is accomplished by the break RESTART; statement. This statement tells the virtual machine to immediately transfer control to the statement labeled RESTART. This will work only if the break statement is nested inside the labeled statement. Otherwise, the compiler will just laugh at you sardonically and tell you that the label RESTART isn't declared. Using labels with continue is slightly different. continue must be nested within the code block of any label to which it refers. Additionally, a continue can transfer control only to a label set on a looping construct such as a for, while, or do statement. For clarification on this rule, look at the following code:

package oracle.hcj.review;
public class SyntaxIssues {
 public static void matrixMeth3(final Point[][] values) {
 LINE: {
 for (int x = 0; x < values[0].length; x++) {
 COLUMN: for (int y = 0; y < values.length; y++) {
 if ((values[x][y].x < 0) || (values[x][y].y < 0)) {
 continue LINE; // skip the rest of the line;
 }
 // do something with the value
 }
 }
 }
 LOGLINE: System.err.println("Invalid Point in Matrix");
 // continue processing
 PASS_TWO: for (int x = 0; x < values[0].length; x++) {
 // do some code.
 }
 }
}


In this example, instead of exiting to the log line, the programmer wants to simply skip the rest of the problematic line in the matrix. Using a continue statement without a label would have merely transferred control to the inner for loop. However, by telling the continue statement to continue at the LINE label, you can exit the inner for loop and skip the rest of the line properly. However, you could not use LOGLINE as a continue target because the statement after the label isn't a loop construct, such as a for, while, or do statement. You also couldn't use PASS_TWO as a target for the continue statement because although the label marks a looping statement, the continue statement is not nested in that looping statement. Furthermore, it would be impossible to break or continue to a label outside of the method. Although labels can be useful in controlling the flow in a program, they can lead to a lot of confusion in development due to their idiosyncrasies. I recommend that you not use them as a rule; however, you should now be able to unwind the spaghetti code of another developer that did use them.

assert

The assert keyword is a new addition to Java that was introduced in JDK 1.4. It was a long overdue addition to the language, as it provides for error checking that C++ and other languages already have. However, since its introduction, I haven't seen it used nearly as much as it should be. The reason for this is a basic lack of understanding as to how assert works. The assert keyword has two forms. The first form of assertion uses the following grammar:

assert Boolean_Expression;


In this form, Expression is replaced by an expression that evaluates to a boolean result. If the result is false, then an AssertionError will be thrown by the compiler:

package oracle.hcj.review;
public class Assertions {
 protected static void helperParseArgs (final String[] args) {
 assert (args != null);
 // . . . code
 }
}


In this example, the programmer wants to make sure that the user of the helperParseArgs( ) method didn't send a null for the arguments parameter. To accomplish this, he uses the assert keyword. If the user passes a null, then the virtual machine will throw an AssertionError with no description, but with a stack trace to help the errant user figure out what he did wrong. If the developer of the method wants to provide an error message, he could have used the second form of assert:

package oracle.hcj.review;
public class Assertions {
 protected static void helperParseArgs (final String[] args) {
 assert (args != null) : "args cannot be null.";
 // . . . code
 }
}


In this case, if the assertion is thrown, the second expression is evaluated and the result of that evaluation is used for the detail message of the AssertionError. Keep in mind that you can use any expression that evaluates to anything other than void as the second expression. For example, the following would be illegal:

package oracle.hcj.review;
public class Assertions {
 protected static void helperParseArgs (final String[] args) {
 assert (args != null) : String s = "args cannot be null.";
 // . . . code
 }
}


An attempt to do something like this would be rejected by the compiler since the evaluation of the expression is void. Similarly, any method that returns void is off limits as well.

Assertions versus exceptions

One of the most common questions asked about assertions is why you should bother using them when you can perform these checks and indicate errors with exceptions. First of all, the code doing the check may be far more complicated than a simple check for null. In fact, the body of assertion expressions can easily be as much work for the processor as the body of the method itself. If you use exceptions, then these checks will be performed every time the method is called. In a production system, this can add significant overhead. On the other hand, with assertions, the virtual machine can be set to ignore the assertions. As a result, their processing time is near zero. In development mode, during testing, or during diagnosis of some nasty problem, you can turn on assertions and figure out the problems. Once the software is stable, you can turn off assertions and gain a performance boost. As a bonus, assertions offer a shorthand that makes your code look more elegant.

Screenshot

Assertions are off by default and have to be turned on manually.


To assert or not to assert

Deciding whether to use assertions or exceptions is a decision that you have to take on a case-by-case basis. Here are some tips that will help you make the right decisions:

  • Don't use assertions to validate parameters of public functions. These functions should throw NullPointerException, IllegalArgumentException, and other relevant exceptions instead. Since public functions will be used by other programmers, you should make sure that they get the right errors if they mess up.
  • Use assertions to check preconditions and postconditions on parameters of protected and private access methods.
  • Don't use assertions to check for software user errors. If you expect the user of your web-based online sales system to enter a 10-digit credit card number and she enters only 9 digits, don't use an assert. Instead, throw IllegalArgumentException. If you use assert, as soon as someone turns off assertions on your servlet container, the checking logic in your system would go away.
  • Use assertions to check parameters and variables for conditions that shouldn't happen. For example, consider the following event handler method:
    package oracle.hcj.review;
    public class Assertions {
     public void mouseClicked(final MouseEvent event) {
     Object source = event.getSource( );
     assert(source != null);
     int hc = source.hashCode( );
     // . . . do code using source
     }
    }
    


    The virtual machine should never pass null as a source for a mouse event. However, just to be sure, the user asserts it. The granularity of assertions that you should use here is something of a judgment call. If you assert everything, you will have to write a lot more code; however, your code will be rock solid.

  • Use assertions to check for invalid code branches. For example, consider the following GUI class:
    package oracle.hcj.review;
    public class Assertions {
    package oracle.hcj.review;
    import java.awt.event.ActionEvent;
    import javax.swing.JButton;
    public class SomeGuiPanel {
     private JButton cancelBtn;
     private JButton noBtn;
     private JButton yesBtn;
     public void actionPerformed(final ActionEvent event) {
     Object source = event.getSource( );
     assert (source != null);
     if (source == yesBtn) {
     // . . . do code
     } else if (source == noBtn) {
     // . . . do code
     } else if (source == cancelBtn) {
     // . . . do code
     } else {
     assert false : "Invalid Source " + source.toString( );
     }
     }
    }
    


    In this class, the action handler expects the user to click on one of three buttons. The method doesn't expect there to be any other buttons to which the class is listening. However, if a developer added a new button but forgot to change the handler, there would be an extra button to deal with. On the final else clause, the method checks to make sure there aren't any other buttons by using assert. If the emphasized line is hit, the program will generate an assertion exception. In this case, using assert is much more elegant than using throw.

    Screenshot

    Some may object that the program should always throw an irrecoverable error at this point regardless of whether assertions are enabled. This is a valid point. However, in a production environment, that is not always a good idea. The accountants using your software wouldn't know what an AssertionError is. Instead, when the program crashes, they are likely to give you an informative bug report such as, "It crashed while I was making my finance report." Its better to use assertions so the program can at least limp along if something weird happens. Let the developers and QA people deal with the assertion errors.


  • Don't use an assertion to do any work. Assertions are developer-level errors and shouldn't be used to repair state in the program or perform complex logging. Also, don't forget that if a user runs the program without assertions, the code will be gone. If that code was critical to the functioning of the program, you could be in deep trouble.
  • Don't bother internationalizing assertion error messages. Again, since assertions are developer-level issues, internationalizing them would be a waste of time.
  • Use assertions to check post conditions. If you create a method and expect that it will never to return null to the user, you might write something like the following:
    package oracle.hcj.review;
    public class SyntaxIssues {
     protected Object someHelperMethod( ) {
     Object result = null;
     // . . . do some code that sets result.
     assert (result != null); // check post condition.
     return result;
     }
    }
    


    In this code, the method checks the return value against a post-condition before returning the value. If the value is still null after all of the work, the assertion will toss an error.

Now that you have a good idea of when to use assert and when not to, apply what you have learned on an actual class:

package oracle.hcj.review;
import java.awt.event.MouseEvent;
import java.util.Arrays;
import java.util.Iterator;
import java.util.List;
public class Assertions {
 public static void main(final String[] args) {
 helperParseArgs(args);
 Iterator iter = System.getProperties( )
 .keySet( )
 .iterator( );
 for (String key = null; iter.hasNext( ); key = (String)iter.next( )) {
 assert (key != null);
 System.out.println(key + "=" + System.getProperty(key));
 }
 }
 protected static void helperParseArgs(final String[] args) {
 assert (args != null);
 assert (!Arrays.asList(args)
 .contains(null));
 // --
 List arglist = Arrays.asList(args);
 Iterator iter = arglist.iterator( );
 for (String argument = null; iter.hasNext( ); argument = (String)iter.next( )) {
 if (argument.startsWith("-D")) {
 if (argument.length( ) < 3) {
 int idx = arglist.indexOf(argument);
 throw new IllegalArgumentException("Argument" + idx
 + " is not a legal property argument.");
 }
 int valueIdx = argument.indexOf('=');
 System.setProperty(argument.substring(2, valueIdx),
 argument.substring(valueIdx + 1));
 assert (System.getProperty(
 argument.substring(2, valueIdx))
 .equals(argument.substring(valueIdx + 1)));
 }
 }
 }
}


In this code, you have a method that parses the arguments to the program and looks for system properties to set. If it encounters an argument such as -Dmy.property=5 on the command line, it will set the system property my.property to the value 5. The method that does the actual work is a protected helper method that checks the arguments to the method for validity using assertions. You should also note that the second check to the method takes quite a bit of work to accomplish since the list has to go through and see whether there are any nulls in the argument list. At production time, however, this work will be removed by turning off assertions. Inside the for loop, the method doesn't use an assertion if the user gives a bad property name because this is an error made by the user of the program and not by the developers of the program. Finally, the last assert statement checks that the system property was actually set properly. Notice that this assert does the exact same string manipulation work as the original setting of the system property. Some performance-minded developers may catch on to this fact and get the bright idea to store the key and value at each iteration of the loop, as shown here:

package oracle.hcj.review;
public class Assertions {
for (String argument = null; iter.hasNext( ); argument = (String)iter.next( )) {
 if (argument.startsWith("-D")) {
 if (argument.length( ) < 3) {
 int idx = arglist.indexOf(argument);
 throw new IllegalArgumentException("Argument" + idx
 + " is not a legal property argument.");
 }
 int valueIdx = argument.indexOf('=');
 String key = argument.substring(2, valueIdx);
 String value = argument.substring(valueIdx + 1);
 System.setProperty(key, value);
 assert (System.getProperty(key).equals(value));
 }
}


Although this will work, it is a bit wasteful. The extra allocations of the key and value variables are needed only if assertions are enabled. If assertions are disabled, then these variables will be used only once. Whether to use variables to store parts of an assertion is a judgment call. If the code that is creating the variable is very long, a variable may be the best bet. If the code is simple, you should just save yourself the extra allocations at production time.

Assertions and deployment

If you are developing an app using assertions, you should be aware that by default, assertions are not enabled in the virtual machine. To enable all assertions, you need to pass the argument -ea to your virtual machine. If you need to enable assertions on system classes, use the switch -esa. Additionally, there are facilities for enabling assertions on individual packages. You should consult your virtual machine documentation for information on how to do this. When developing a product, you should always have assertions enabled, as they will help you catch bugs. Also, your QA testers should run a program with assertions enabled since it is their job to figure out how to break your code. In a production release, the situation is a bit foggier. In the first days of a production product release, you may want to make sure the project runs with assertions enabled. Although you will loose some performance and incur a few crashes, you will be able to detect many errors caught by your assertions. When to turn off the assertions in a production environment is a judgment call that the project manager has to make. There will come a time when you think you have used the assertions enough to catch most of the errors, and performance and stability become the overriding need; after all, assertions do crash your program. At that point, it is time to update your shortcuts and batch files to turn off assertions. When releasing new features, you should employ a similar operating strategy. This will help you catch unexpected bugs. Unfortunately, there is no way to turn assertions on and off in a running system; however, you can easily find out whether they are on by using the little assertion trick shown here:

boolean ASSERTIONS = false;
assert ASSERTIONS = true;


The assert expression here evaluates to true and will not throw an assertion error at any time. If assertions are enabled, then the code will run and ASSERTIONS will be set to true. If assertions are disabled, then the code won't be executed and ASSERTIONS will remain false.

Chaining Constructors

One of my personal pet-peeves is duplicated code. Unfortunately, you see a lot of it, especially in the creation of GUI widgets. The problem results from the way these widgets are built. Consider the button class shown in Example 1-6.

Example 1-6. Unchained constructors
package oracle.hcj.review;
public class UnchainedConstructors extends JButton implements ActionListener {
 public UnchainedConstructors(final String text) {
 setText(text);
 String tooltip = new String("A button to show " + text);
 setToolTipText(tooltip);
 }
 public UnchainedConstructors(final String text, final String tooltip) {
 setText(text);
 setToolTipText(tooltip);
 }
 public UnchainedConstructors(final String text, final String tooltip,
 final ActionListener listener) {
 setText(text);
 setToolTipText(tooltip);
 addActionListener(listener);
 }
}


Although this code will work, it is wasteful and prone to errors. For example, if you change a line in the constructor that accepts three parameters and forget to make the same change in the other constructors, you would have a button that builds correctly under some circumstances, but fails to build correctly under others. This would lead to a debugging session that wouldn't be much fun. The symptom of the problem is in the thought, "If I change this in one constructor, I have to remember to change it in the others." Whenever you hear yourself thinking similar thoughts, you should realize that something is wrong. All of your code dependencies should be in the code, and not in your head. Inevitably, you will forget some of these dependencies during your project. Also, even if you have a photographic memory and perfect recall, the junior programmer working for you won't be privy to these details and could unintentionally bust the code you had so well thought out. To avoid having to remember these things, you can rewrite the class as shown in Example 1-7.

Example 1-7. Simple chained constructors
package oracle.hcj.review;
public class ChainedConstructors extends JButton implements ActionListener {
 public ChainedConstructors(final String text) {
 this(text, new String("A button to show " + text), null);
 }
 public ChainedConstructors(final String text, final String tooltip) {
 this(text, tooltip, null);
 }
 public ChainedConstructors(final String text, final String tooltip,
 final ActionListener listener) {
 setText(text);
 setToolTipText(tooltip);
 if (listener != null) {
 addActionListener(listener);
 }
 }


This code is much better; it chains the constructors so that each delegates to the constructor that takes all parameters. This constructor is referred to as the primary constructor. By using this technique, the code for the other constructors is limited only to what they do differently. This technique relieves you of having to remember to change each constructor if the common code is modified. You chain the constructors via the this( ) method. This special method allows you to call one constructor from another. It is analogous to using the super( ) method to construct base classes. The only remaining problem is that you have some rather ugly code as a result of chaining your constructors:

this(text, new String("A button to show " + text), null);


This code results from the fact that, just like super( ), you have to use the this( ) method as the first line in the constructor. I have yet to understand why this limitation is in Java, but it does indeed exist. As a result, some rather annoying problems are introduced. In this case, you were merely building a String object in the call to this( ). However, there are other cases where you will need to do a lot more work before calling the primary constructor. The secret to doing this without the ugliness is via helper methods:

package oracle.hcj.review;
public class ChainedConstructors extends JButton implements ActionListener {
 public ChainedConstructors(final String text, final boolean showDate) {
 this(text, buildDateToolTip(text, showDate), null);
 }
 private static String buildDateToolTip(final String text, final boolean showDate) {
 final StringBuffer buf = new StringBuffer(250);
 buf.append("A panel to show ");
 buf.append(text);
 if (showDate) {
 buf.append(" created on ");
 DateFormat df = DateFormat.getInstance( );
 buf.append(df.format(Calendar.getInstance( ).getTime( )));
 }
 return buf.toString( );
 }
}


Here, you create a new static method that manipulates the input to the constructor and returns the tool tip text. Then you use the resulting text as the input to the tooltip parameter of the primary constructor. This constructor helper technique makes your code much cleaner and allows you to chain constructors even when there is complex logic within the constructors. The resulting code is much more stable and easier to maintain. When using helper methods, it is important that you keep in mind one restriction: the this pointer is not available to the virtual machine at the time of a this( ) or super( ) call. The result of this restriction is that you can't use any instance-scoped methods on the object being constructed. For example, the following code would be illegal:

public class ChainedConstructors extends JButton implements ActionListener {
 public ChainedConstructors(final Class dataType) {
 this(buildClassText(dataType), dataType.getName( ), this);
 }
 protected String buildClassText(final Class dataType) {
 return dataType.getName( ).substring(dataType.getName( )
 .lastIndexOf('.') + 1);
 }
}


In Java, a call such as buildClassText( ) is identical to the call this.buildClassText( ). The this reference is assumed unless you explicitly add some other kind of reference, such as an object or a class. In this case, the this pointer is not available yet, so you can't call instance-scoped methods on this object. In fact, even though the class ChainedConstructors implements ActionListener, you can't even use the this pointer to pass the object as a listener to itself. If you uncomment the above constructor in the code and try to compile it, you will get the following results:

>ant -Dexample=oracle/hcj/review/ChainedConstructors.java compile_example compile_example:
 [javac] Compiling 1 source file to C:\dev\hcj\bin
  [javac] C:\dev\hcj\src\oracle\hcj\review\ChainedConstructors.java:43: cannot reference this before supertype constructor has been called
 [javac] this(buildClassText(dataType), dataType.getName( ), this);
 [javac] ^
  [javac] C:\dev\hcj\src\oracle\hcj\review\ChainedConstructors.java:43: cannot reference this before supertype constructor has been called
 [javac] this(buildClassText(dataType), dataType.getName( ), this);
 [javac] ^
 [javac] 2 errors


In short, the this pointer is off-limits to all constructor helper methods. Therefore, if you want to declare a helper method, you must make sure that the method is declared static. Since a static method doesn't have a this pointer and doesn't need one, the call will work properly. However, keep in mind that instance methods on objects already in scope are completely permissible, as shown here:

public ChainedConstructors(final Color color) {
 this(color.toString( ), "", null);
}


In this example, the instance method toString( ) on the color parameter passed to you is legal because it is already in scope. While using chained constructors, you will come across situations in which you do not want to expose the primary constructor to the user of the class. The solution to this problem is to merely make the visibility of the primary constructor protected. That way, you get the best of both worlds. The constructors will be chained, and you won't reveal any more about the class than you want to. Although it is unusual to see a class with constructors that have various visibilities, it is quite legal.

Initialization

Initialization is the process of assigning values to variables prior to the execution of the constructor for a class. Initialization is used in many places, and is often poorly understood by developers. Many developers think of initialization as one topic, when, in fact, there are six types of initialization that you can use in your code. The simplest type of initialization is shown here:

package oracle.hcj.review;
public class InitalizerDemo {
 public String description = "An initialized member";
}


The variable description in this class is assigned the value of "An initialized member" just prior to construction of each instance of the object. Therefore, even if the constructor does not explicitly set description, it will be guaranteed to contain the value set in the initializer. This type of initialization is important for solidifying code because it allows you to preset instance attributes to default values before the constructor is called. In this manner, constructors can ignore these attributes if they wish. Although this type of initialization is useful, there are times when initializations need to be a bit more complex:

package oracle.hcj.review;
public class InitalizerDemo {
 public long timestamp = System.currentTimeMillis( );
}


In this initializer, you can call a method on the line of the initialization. This ability allows you to perform more complex initializations that would be impossible without a method. However, keep in mind that methods used in initializers are under the same restrictions as the helper methods for chained constructors that we discussed earlier—they should be static and can't use the this pointer. In fact, you don't even need to define a helper method if you don't want to; an initializer can be a method itself:

package oracle.hcj.review;
public class InitalizerDemo {
 private String xmlClasspath;
 {
 final StringBuffer buf = new StringBuffer(500);
 final String classPath = System.getProperty("java.class.path");
 StringTokenizer tok = new StringTokenizer(classPath,
 System.getProperty("path.separator"));
 buf.append("<classpath>\n");
 while (tok.hasMoreTokens( )) {
 buf.append(" <pathelement location=\"");
 buf.append(tok.nextToken( ));
 buf.append("\"/>\n");
 }
 buf.append("</classpath>");
 xmlClasspath = buf.toString( );
 }
}


In this code, you translate the classpath into an XML structure upon initialization of the object. This code will work fine, as you can see by running the oracle.hcj.review.InitializerDemo class in the example code:

>ant -Dexample=oracle.hcj.review.InitializerDemo run_example run_example:
 [java] ------Dumping Contents-----------
 [java] ---------------------------------
 [java] Initializer Demo
 [java] x86
 [java] C:\Documents and Settings\Robert
 [java] An initialized member
 [java] <classpath>
 [java] <pathelement location="c:\j2sdk\\lib\tools.jar"/>
 [java] <pathelement location="C:\j2sdk\addons\jakarta-ant-1.5\bin\\..\lib\xml-apis.jar"/>
 [java] <pathelement location="C:\j2sdk\addons\jakarta-ant-1.5\bin\\..\lib\xercesImpl.jar"/>
 [java] <pathelement location="C:\j2sdk\addons\jakarta-ant-1.5\bin\\..\lib\optional.jar"/>
 [java] <pathelement location="C:\j2sdk\addons\jakarta-ant-1.5\bin\\..\lib\ant.jar"/>
 [java] </classpath>
 [java] ---------------------------------


Although this initialization technique is useful, you shouldn't use it routinely. Instead, it should be saved for special circumstances, as it makes code a little cryptic to read. Use it only when you really need a special multiline initialization. Instance attributes aren't the only kind of attributes in a class. Attributes declared with the static keyword are class-scoped. Class-scoped attributes can be initialized in a manner similar to instance-scoped attributes. Here is a simple case:

package oracle.hcj.review;
public class InitalizerDemo {
 public static final String NAME = "Initializer Demo";
}


This code works exactly like the instance initializer, except that it is called when the class is loaded into the virtual machine by the ClassLoader. Another difference is that this attribute is final, so it cannot be changed after it is initialized.

Screenshot

Although I show final class-scoped attributes in the examples, keep in mind that the behavior of class-scoped attributes that are not final is identical to that of final class-scoped attributes.


Like instance initializers, you can call methods in static variable initialization:

package oracle.hcj.review;
public class InitalizerDemo {
 public static final String ARCH = System.getProperty("os.arch");
}


You can also defer an initialization of a static variable to do something more complex:

package oracle.hcj.review;
public class InitalizerDemo {
 public static final String USER_HOME;
}


In this code, you have created a constant without giving the constant a value. Perhaps the value you want requires more complex code than a simple assignment. This is where the special static{} method comes in. The ClassLoader automatically invokes all static{} initializer methods when the class is loaded into the virtual machine. It is in this method only that you can initialize final static variables that are not initialized on their declaration line. You can use this method in a similar manner as the method-based instance initializers:

package oracle.hcj.review;
public class InitalizerDemo {
 public static final String USER_HOME;
 static {
 USER_HOME = System.getProperty("user.home");
 }
}


The variable USER_HOME will be filled with the string version of the user's home directory from the system properties. The value will then be final for the duration of the life of the virtual machine. While using the static{} method, it is important to keep order in mind. Since static initializers are called in order of their declaration, you can get into a bit of trouble if you aren't careful:

public static class Values {
 public final static String VALUE_ONE = "Blue";
 static {
 System.out.println("static{} method for One");
 System.out.println(VALUE_ONE);
 System.out.println(VALUE_TWO); // <= compiler error
 }
 public final static String VALUE_TWO = "Red";
}


In this code, the compiler cannot find VALUE_TWO because it has not been declared at the time when the static{} method was run. Since initializers are processed in order, you get a bug called an illegal forward reference. Get rid of the bug with a coding standard that declares all static variables first, and then declares static methods. This will keep you out of trouble with your compiler (although not necessarily with your coding logic). While order is important, be aware that you cannot depend on the order of static initialization. Any methods that depend on ordering are likely to end up with some strange results. To make things a bit clearer, let's intentionally build an example of this problem. Example 1-8 shows the constructed bug.

Example 1-8. Erroneous dependence on static initializer order
package oracle.hcj.review;
public class StaticOrderDemo {
 public StaticOrderDemo( ) {
 }
 public static final void main(final String[] args) {
 }
 public static class Ranges {
 public static final String[] RANGE_BLUE = { "Sky", "Navy" };
 public static final String[] RANGE_RED = { "Light", "Dark" };
 static {
 System.out.println("static{} method for Ranges");
 System.out.println(Arrays.asList(RANGE_BLUE));
 System.out.println(Values.VALUE_SPECIFIER);
 System.out.println(Arrays.asList(RANGE_RED));
 }
 }
 public static class Values {
 public static final String VALUE = "Blue";
 public static final String VALUE_SPECIFIER;
 static {
 System.out.println("static{} method for Values");
 System.out.println(VALUE);
 System.out.println(Ranges.RANGE_BLUE);
 VALUE_SPECIFIER = Ranges.RANGE_BLUE[1];
 }
 }
}


When the example is run, the static nested class Ranges is initialized first. As a result, you will see the following output:

>ant -Dexample=oracle.hcj.review.StaticOrderDemo run_example run_example:
 [java] static{} method for Values
 [java] Blue
 [java] static{} method for Ranges
 [java] [Sky, Navy]
 [java] null
 [java] [Light, Dark]
 [java] [Ljava.lang.String;@1e3118a
 [java] Class oracle.hcj.review.StaticOrderDemo$Values Loaded


Note especially the null. Since Values.VALUE_SPECIFIER had not been initialized when it was used in the Ranges initializer, its value was null at the time of the System.out.println( ). Only microseconds later, when Values finishes initializing, it is no longer null. This kind of bug is very difficult to find because of its very transient life. The moral of this story is that you can never depend on the initialization order of static members.

Screenshot

Although this example was specifically constructed for this tutorial, I actually encountered this bug in production code several times. The first time resulted in an overnight coding frenzy to try to find and correct the bug.


To summarize, there are many kinds of initializers, which are all useful for setting values prior to object construction, but beware of the little gotchas with static initializers. In Example 1-9, all of the possible initializers are shown in one class.

Example 1-9. All the various initializers together
package oracle.hcj.review;
public class InitalizerDemo {
 /** Simple static initialization. */
 public static final String NAME = "Initializer Demo";
 /** Initialized static on one line. */
 public static final String ARCH = System.getProperty("os.arch");
 /** Static method based initialization. */
 public static final String USER_HOME;
 static {
 USER_HOME = System.getProperty("user.home");
 }
 /** Simple instance member initialization. */
 public String description = "An initialized member";
 /** Method call instance member initialization. */
 public long timestamp = System.currentTimeMillis( );
 /** Complex instance member initialization. */
 private String xmlClasspath;
 {
 final StringBuffer buf = new StringBuffer(500);
 final String classPath = System.getProperty("java.class.path");
 StringTokenizer tok =
 new StringTokenizer(classPath,
 System.getProperty("path.separator"));
 buf.append("<classpath>\n");
 while (tok.hasMoreTokens( )) {
 buf.append(" <pathelement location=\"");
 buf.append(tok.nextToken( ));
 buf.append("\"/>\n");
 }
 buf.append("</classpath>\n");
 xmlClasspath = buf.toString( );
 }
}


One final thing to remember about all initializers is that you cannot throw any exceptions other than subclasses of RuntimeException within the initializer. All other exceptions will cause all sorts of compilation errors. Therefore, if you want to use methods that throw other types of exceptions, you will have to wrap up any possible exceptions in a try and catch block and then throw a RuntimeException; of course, you're caught up on chained exceptions by now, so this is no problem. With this arsenal of initializers at your command, you should be ready to tackle setting defaults for any type of class you can think of.

      
Comments