The main principle

Write clean, simple, elegant code.

Corollary. If your code is ugly, it is probably wrong. You need to rethink your approach.



Minimize object creation

Some programmers, especially those new to object orientation, think that you should create an object for everything. This is wasteful.

Remember the purpose of an object: to hold data over time, and possibly associate behavior with that data. If you just need a place to hold some code, and are not concerned about overriding it, or providing different versions of it, put it in a static method and call it statically. There is no need to create an object just to call a method on it. If you can, especially avoid creating an object, calling one method on it, then immediately discarding it.

Object creation takes time and heap space. Subsequent garbage collection also takes time. Creating objects unnecessarily wastes both space and time.

Where it makes sense, instead of

 new MyClass().myInstanceMethod();
 

write

 MyClass.myStaticMethod();
 



Prefer interfaces to static methods

This sounds like it is going to contradict the previous advice.

It makes testing easier if you express behavior in interfaces rather than in static methods. This applies to behavior that you want to change, say to a mock or dummy version, for testing. If you use static methods in a utility class, you have to modify the code that calls them to replace them with test versions. If you describe the behavior by an interface, you can put the real version in one class and the test version in another class, then switch between them as you wish using configuration files.



Transforming a test program to a real program

You can often start with a test program, and then transform it step by step to become the real program. Each step of the transformation shows you more plainly what the real program should be.



Avoid redundant boolean comparisons

Test conditions in if, while, and for statements are already boolean. There is no need to do comparisons to true and false. Using them is a holdover from C.

Instead of

 if (flag == true)
 {
   ...
 }
 

write

 if (flag)
 {
   ...
 }
 

Instead of

 if (flag == false)
 {
   ...
 }
 

write

 if (!flag)
 {
   ...
 }
 



Always enclose conditional blocks in braces

Always put braces around then, else, while, and for blocks, even if the block is a single statement. This prevents unintentional bugs if you add code to the blocks later.

Instead of

 if (x != null)
   process(x);
 

write

 if (x != null)
 {
   process(x);
 }
 

This avoids errors like

 if (x != null)
   System.out.println("Processing");
   process(x);  // Oops!  Now the processing occurs even if x is null!
 



Make variables final: treat them as names

Think of variables as names for values rather than places to store values. Many variables never change once they are set. Make all variables final unless you intend to change them. It is a good discipline to make all method parameters final, period. This accomplishes several purposes:

  • It allows the compiler to check for inadvertent changes to the variable.
  • It serves as a visual clue to human readers which variables you intend to change.
  • It simplifies the job of a human reader by associating one concept with one variable instead of trying to share several conceptually different values in the same variable. Let the compiler's register allocator sort all that out.
  • It keeps you from having to worry about making variables final when they are referenced in a local inner class: they are already final!
There are a few cases where variables cannot be final:

  • Variables that must change, such as loop indices.
  • Variables that you must initialize in some way, then set to some default or freshly generated value if the initialization fails. One example is looking a value up in a map, then generating a new value to put back into the map if it is not there. You could avoid the non-final variables in this case, but it complicates the code.
You will find, as you do this, that the way you think about using variables will change.

This advice applies only to classes. Do not make method parameters parameters in interfaces final. There is no need.



Minimize variable creation

If we think of variables primarily as names for values rather than as storage locations, we realize that not every value needs a name. This is because many values are created and used only once in the context that they are created. For example, a value may be merely passed to a method or returned from a method, or used as the target of a method call. The general idea is that you do not need to store a value in a variable unless you need to refer to it more than once in the context in which it is created. This simplifies the generated code and the virtual machine's job of register allocation.

Instead of

 public int getIntProperty(final String key)
 {
   final String stringValue = Syststem.getProperty(key);
   final int intValue = Integer.parseInt(stringValue);
   return intValue;
 }
 

write

 public int getIntProperty(final String key)
 {
   return Integer.parseInt(Syststem.getProperty(key));
 }
 

There are exceptions to this. Somtimes, the code to create a variable, though just a series of method calls, is long and complex enough that you can improve readability by storing an intermediate result in a variable, then using it to calculate the final result.



Interfaces

There is no need to declare method parameters in interfaces final.
There is no need to declare variables in interfaces public, static, or final, since they already are.
There is no need to declare methods in interfaces public, since they already are.



Keep parts of an array type name together

When writing array types, put the brackets after the type name rather than after the variable name as in C. This keeps the type name together and allows the reader to tell more quickly that it is an array. For "multidimensional" arrays, put all the pairs of brackets after the type name.

Instead of

 String strings[];
 int[] values[];  // or int values[][];
 

write

 String[] strings;
 int[][] values;
 



Use trailing commas in array initializers

When writing array initializers, put a comma after the last value as well as all the others. This is legal and makes it easier to add more values later without forgetting the comma and getting compile errors. Of course, many development environments that pre-parse make the error obvious immediately.

Instead of

 int[] values = { 3, 6, 9 };
 

write

 int[] values = { 3, 6, 9, };
 



Avoid tests that are known to be true once

Try very hard to avoid writing tests that you know in advance will be true exactly once and false the rest of the time, or vice versa, and you know when the unique case will occur. A common example is some condition that will be true the first (or last) iteration through a loop and false the rest of the iterations. Restructure the code to avoid the test and the extra time it takes.

Instead of (admittedly contrived):

 // Initialize first element of array to 1, other elements to 2.
 int[] values = new int[100];
 for (int ix = 0; ix < values.length; ix++)
 {
   // This test is true exactly once, on the first iteration.
   if (ix == 0)
   {
     // This code is run exactly once, on the first iteration.
     values[ix] = 1;
   }
   else
   {
     // This code is run on every iteration except the first.
     values[ix] = 2;
   }
 }
 

write

 // Initialize first element of array to 1, other elements to 2.
 int[] values = new int[100];
 values[0] = 1;
 for (int ix = 1; ix < values.length; ix++)
 {
   values[ix] = 2;
 }
 

or even better, in this case:

 import java.util.Arrays;
 // ...
 // Initialize first element of array to 1, other elements to 2.
 int[] values = new int[100];
 values[0] = 1;
 Arrays.fill(values, 1, values.length, 2); 
 



Check for illegal values early

Say you are constructing an object, and certain arguments to the constructor must not be null because they would cause a null pointer exception when used later by the object's methods. Validate the arguments and throw an IllegalArgumentException immediately from the constructor. Probably you created the null value in the chain of calls that led up to the constructor call. It will be easier to determine the true source of the problem by examining a stack trace here than by trying to follow a stack trace leading to the method that throws a NullPointerException later on, when the source of the null value may not be at all evident.



Test code is interesting

Test code is sometimes the most interesting code that you write, since you are trying to simulate the real pieces that you do not want included in your test.



Check your printing code

You have code that is printing something out. What is printed is not what you expected. Check your printing code. It may be that what you are printing is correct, but you are printing it incorrectly. You may be printing the wrong part of it, or printing it through a method that you did not expect. This can happen when you use the visitor pattern and your code is compiled in a strange order.



Do not write trivial overrides

When you are writing a subclass of a class that defines a method, you should almost never write an overridden version of the method that just calls its superclass method.

   void foo()
   {
     super.foo();
   }
 

This is unnecessary, and even wasteful. The class you are writing will inherit the method from its superclass anyway. Writing a "trivial override" like this just wastes space by storing the unnecessary method, and time by adding an extra unnecessary call.

Some development tools may automatically generate methods like this. Check for them and remove them.

There is only one case when you would want to write such a method. That is when you need to increase the visibility of the superclass method. For example, say you are subclassing a class that someone else wrote. You may want to make a protected or package-private (why not private?) method public so that other classes can use it.

 public class A
 {
   protected void foo()
   {
     // ...
   }
 }
 
 public class B extends A
 {
   public void foo()
   {
     super.foo();
   }
 }
 

For example, the class java.util.Observable has protected methods clearChanged() and setChanged(). You might want a subclass that makes them public.


Page Information

  • 1 year ago [history]
  • View page source
  • You're not logged in
  • No tags yet learn more

Wiki Information

Recent PBwiki Blog Posts