Draft CDF Online Java Development Standards

Java Development Environment

Java version 1.1.x is recommended. New code should avoid using features from the 1.0.x version, mainly in the graphical interface and I/O areas, that are deprecated.

For Windows and Solaris operating systems, the Java Development Kit (JDK) version 1.1.5 from Sun/Javasoft is recommended. For Solaris systems running 2.6 of the operating system, the 1.1.4 JDK from SunSoft is recommended. This version includes a JIT for much better performance, and uses operating system threads, which may be necessary for applications that use sockets in "C" code. For Windows systems, the performance pack add-on seems to work OK.

For SGI systems running IRIX 5.3, the JDK for IRIX version 3.0.1 (based on Sun JDK 1.1.3) from SGI is recommended. For SGI systems running IRIX 6.X, the JDK for IRIX version 3.1 (based on Sun JDK 1.1.5) is recommended.

The Microsoft Java Software Development Kit (SDK) is not recommended. It uses a different interface to C code, has some subtle incompatibilities with the Sun versions and does not support the complete JDK 1.1 class libraries.

Integrated Developer Environments

Integrated Developer Environments (IDEs) typically contain an editor, class browser, compiler, debugger, project manager, and graphical interface builder in an integrated package. As the command line debugger with the Sun JDK is difficult to use, such packages are the only way to obtain a decent debugger. However due to the dynamic loading feature of Java it is possible to get by without a debugger. For Windows systems, the Symantec Visual Cafe 2.0 package is recommended. Microsoft Visual J++ uses the Microsoft interpreter and so is not recommended for the above mentioned reasons. Borland JBuilder is conceptually very promising. However the initial release seems a bit quirky and is unfortunately difficult to recommend for general use. Java Workshop is the only reasonable environment for Solaris systems, but is inferior to the above. It may be used on Windows systems as well as Solaris. SGI has recently released a new version of their CosmoCode package, and made it available for Windows NT as well as IRIX. This version has not yet been evaluated. As the interface builders mentioned above in their automatic code generation tend to import their own graphics class libraries, use of the interface builder portions is not recommended for general applications. Various other IDEs have been evaluated and are not recommended for various reasons.

Graphical User Interface Class Libraries

Version 1.0.1 of the Java Foundation Classes (aka "Swing") has been released. This is the primary recommended GUI library. Note that while mixing of core AWT as well as other third party widgets will work at some level, there will be odd effects and the performance will be dramatically lower. Best performance is obtained by using only JFC widgets, and making use of the double buffering option. The JFC infrastructure is then able to track what parts of a display must be (re)drawn.

Documentation may be found on the Sun web site:

Aside from their enhanced functionality, another distinguishing aspect of the JFC and JClass packages is their components are not built on top of the analogous Motif or Windows native components. Instead the components are built from primitive graphics operations using Java code. Hence they will look the same on both unix and Windows platforms, and are more likely to function the same on both as well. And the JFC features a "pluggable look-and-feel" capability in that an application may adopt the "look-and-feel" of Motif, Windows 95, Mac, or various other options with only a single initialization call.

Use of the Microsoft AFC is not recommended. The version released with Internet Explorer 4.0/Microsoft SDK 2.0 only works with the Microsoft JVM. Also, some care is required in mixing it with the core AWT and other 3rd party packages.

Coding Standards

The purpose of the recommendations below are to help write code that can be developed and debugged more rapidly, by avoiding a number of common pitfalls. Also, following them will result in code that is more easily maintained and updated in the future.

Many of these recommendations are adapted from Doug Lea as of December 31, 1996. Note there are some differences however.

See also suggested coding standards from Sun

The form of those guidelines is based on example coding standards and checklists in Watts Humphrey's book A Discipline for Software Engineering, Addison-Wesley, 1995, along with feedback (to Doug Lea) from people using previous versions of this document.

Additional useful guidelines may be found in Appendix C of Eckel's book which is available on the web.

Structure and Documentation

Packages

Create a new java package for each self-contained project or group of related functionality. Create and use directories in accord with java package conventions. Note that if a product contains multiple subpackages of some package, the source directory name must end with the complete package name in order for javadoc to generate fully cross-referenced documentation. For example for package x.y.z, the source directory should end in .../x/y/z.

Package names for CDF online products that are intended for possible use outside the lab (such as FISION) should begin with gov.fnal. Package names for products that will not be exported beyond CDF need not begin with anything special. Packages that may be used elsewhere at the lab should perhaps begin with cdf. Each "product" should have a separate package name. Sometimes it may be worthwhile for a single product to contain multiple sub-packages.

Consider writing an index.html file in the doc sub-directory of a product briefly outlining the purpose and structure of the package.

Program Files

Place each class in a separate file. This applies even to non-public classes (which are allowed by the Java compiler to be placed in the same file as the main class using them) except in the case of one-shot usages where the non-public class cannot conceivably be used outside of its context.

Begin each file with a comment including:

  1. The file name and/or related identifying information.
  2. If the file introduces a principal entry point for a package, briefly describe the rationale for constructing the package.

Immediately follow each file header with:

Example:

/*
   File: Example.java
*/

package demo;
import java.util.NoSuchElementException;

Classes and Interfaces

Write all /** ... */ comments using javadoc conventions.

Preface each class with a /** ... */ comment describing the purpose of the class, guaranteed invariants, usage instructions, and/or usage examples. Also include any reminders or disclaimers about required or desired improvements. Use HTML format, with added tags:

Example:

/**
 * A class representing a window on the screen.
 * For example:
 * <pre>
 * 	Window win = new Window(parent);
 *	win.show();
 * </pre>
 *
 * @see		awt.BaseWindow
 * @see		awt.Button
 * @version	1.2 31 Jan 1995
 * @author 	Bozo the Clown
 */
class Window extends BaseWindow {
	...
}

Class Variables

Use javadoc conventions to describe nature, purpose, constraints, and usage of instances variables and static variables. Use HTML format, with added tags:

Example:

    /**
     * The current number of elements.
     * must be non-negative, and less than or equal to capacity.
     */
    protected int count_;

Methods

Use javadoc conventions to describe nature, purpose, preconditions, effects, algorithmic notes, usage instructions, reminders, etc. Use HTML format, with added tags:

Be as precise as reasonably possible in documenting effects.

Local declarations, statements, and expressions

Use /* ... */ comments to describe algorithmic details, notes, and related documentation that spans more than a few code statements.

Example:

    /*
     * Strategy:
     *    1. Find the node
     *    2. Clone it
     *    3. Ask inserter to add clone 
     *    4. If successful, delete node
     */

Use Running // comments to clarify non-obvious code. But don't bother adding such comments to obvious code; instead try to make code obvious!

Example:

int index = -1; // -1 serves as flag meaning the index isn't valid

Use any consistent set of choices for code layout, including:

Naming Conventions

packages
lowercase.
Conventions are discussed above.
files
The java compiler enforces the convention that file names have the same base name as the public class they define. This makes source files very easy to locate. Please adhere to this and do not defeat it by making classes non-public.
classes:
CapitalizedWithInternalWordsAlsoCapitalized
Exception class:
ClassNameEndsWithException.
Interface. When necessary to distinguish from similarly named classes:
InterfaceNameEndsWithInterface.
Class. When necessary to distinguish from similarly named interfaces:
ClassNameEndsWithImpl
constants (finals):
UPPER_CASE_WITH_UNDERSCORES
private or protected instance variables:
trailingUnderscore_ (Yes, this is ugly. It is good that it is ugly!)
private or protected static (class) variables:
twoTrailingUnderscores__
public instance variables:
Shouldn't be any.
local variables
firstWordLowerCaseButInternalWordsCapitalized
methods:
firstWordLowerCaseButInternalWordsCapitalized()
factory method for objects of type X:
newX
converter method that returns objects of type X:
toX
method that reports an attribute x of type X:
X getX(). (Adhere to javabeans specification)
method that changes an attribute x of type X:
setX(X value). (Adhere to javabeans specification)

Recommendations

Be precise about what you are importing. Check that all declared imports are actually used.
Rationale: Otherwise readers of your code will have a hard time understanding its context and dependencies.
When sensible, consider writing a main for the principal class in each program file. The main should provide a simple unit test or demo.
Rationale: Forms a basis for testing. Also provides usage examples.
For self-standing application programs, the class with main should be separate from those containing normal classes.
Rationale: Hard-wiring an application program in one of its component class files hinders reuse.
Consider writing template files for the most common kinds of class files you create.
Rationale: Simplifies conformance to coding standards.
If you can conceive of someone else implementing a class's functionality differently, define an interface, not an abstract class. Generally, use abstract classes only when they are ``partially abstract''; i.e., they implement some functionality that must be shared across all subclasses.
Rationale: Interfaces are more flexible than abstract classes. They support multiple inheritance and can be used as `mixins' in otherwise unrelated classes.
When extending the functionality of a class, favor the use of composition vs inheritance where appropriate.
Rationale: This tends to result in much more flexible designs. Especially in Java, which does not support full multiple implementation inheritance.
Make separate classes for display and acquisition/manipulation of data.
Rationale: Provides for better code reuse and minimizes duplication. The Java 1.1 event model provides excellent facilities for implementing a "Model/View/Controller" architecture.
Consider whether any class should implement Cloneable and/or Serializable.
Rationale: These are ``magic'' interfaces in Java, that automatically add possibly-needed functionality only if so requested.
Declare a class as final only if it is a subclass or implementation of a class or interface declaring all of its non-implementation-specific methods. (And similarly for final methods).
Rationale: Making a class final means that no one ever has a chance to reimplement functionality. Defining it instead to be a subclass of a base that is not final means that someone at least gets a chance to subclass the base with an alternate implementation. Which will essentially always happen in the long run.
Never declare instance variables as public.
Rationale: The standard OO reasons. Making variables public gives up control over internal class structure. Also, methods cannot assume that variables have valid values.
Minimize reliance on implicit initializers for instance variables (such as the fact that reference variables are initialized to null).
Rationale: Minimizes initialization errors.
Minimize statics (except for static final constants).
Rationale: Static variables act like globals in non-OO languages. They make methods more context-dependent, hide possible side-effects, sometimes present synchronized access problems. and are the source of fragile, non-extensible constructions. Also, neither static variables nor methods are overridable in any useful sense in subclasses.
Use final and/or comment conventions to indicate whether instance variables that never have their values changed after construction are intended to be constant (immutable) for the lifetime of the object (versus those that just so happen not to get assigned in a class, but could in a subclass).
Rationale: Access to immutable instance variables generally does not require any synchronization control, but others generally do.
Generally prefer protected to private.
Rationale: Unless you have good reason for sealing-in a particular strategy for using a variable or method, you might as well plan for change via subclassing. On the other hand, this almost always entails more work. Basing other code in a base class around protected variables and methods is harder, since you you have to either loosen or check assumptions about their properties. (Note that in Java, protected methods are also accessible from unrelated classes in the same package. There is hardly every any reason to exploit this though.)
Avoid unnecessary instance variable access and update methods. Write get/set-style methods only when they are intrinsic aspects of functionality.
Rationale: Most instance variables in most classes must maintain values that are dependent on those of other instance variables. Allowing them to be read or written in isolation makes it harder to ensure that consistent sets of values are always used.
Minimize direct internal access to instance variables inside methods. Use protected access and update methods instead (or sometimes public ones if they exist anyway).
Rationale: While inconvenient and sometimes overkill, this allows you to vary synchronization and notification policies associated with variable access and change in the class and/or its subclasses, which is otherwise a serious impediment to extensiblity in concurrent OO programming. (Note: The underscore convention for instance variables serves as an annoying reminder of such issues.)
Avoid giving a variable the same name as one in a superclass.
Rationale: This is usually an error.
Prefer declaring arrays as Type[] arrayName rather than Type arrayName[].
Rationale: The second form is just for incorrigible C prgrammers.
Ensure that non-private statics have sensible values even if no instances are ever created. (Similarly ensure that static methods can be executed sensibly.) Use static intitializers (static { ... } ) if necessary.
Rationale: You cannot assume that non-private statics will be accessed only after instances are constructed.
Write methods that only do ``one thing''. In particular, separate out methods that change object state from those that just rely upon it. For a classic example in a Stack, prefer having two methods Object top() and void removeTop() versus the single method Object pop() that does both.
Rationale: This simplifies (sometimes, makes even possible) concurrency control and subclass-based extensions.
Use care when overloading methods on argument type. (Overriding on arity is OK, as in having a one-argument version versus a two-argument version). If you need to specialize behavior according to the class of an argument, consider instead choosing a general type for the nominal argument type (often Object) and using conditionals checking instanceof. Alternatives include techniques such as double-dispatching, or often best, reformulating methods (and/or those of their arguments) to remove dependence on exact argument type.
Rationale: Java method resolution is static; based on the listed types, not the actual types of argument. This is compounded in the case of non-Object types with coercion charts. In both cases, most programmers have not committed the matching rules to memory. The results can be counterintuitive; thus the source of subtle errors. For example, try to predict the output of this. Then compile and run.
class Classifier {
  String identify(Object x)  { return "object"; }
  String identify(Integer x) { return "integer"; }
}

class Relay {
  String relay(Object obj) { return (new Classifier()).identify(obj); }
}

public class App {
  public static void main(String[] args) {
    Relay relayer = new Relay();
    Integer i = new Integer(17);
    System.out.println(relayer.relay(i));
  }
}
     
Prefer synchronized methods to synchronized blocks.
Rationale: Better encsapsulation; less prone to subclassing snags; can be more efficient.
If you override Object.equals, also override Object.hashCode, and vice-versa.
Rationale: Essentially all containers and other utilities that group or compare objects in ways depending on equality rely on hashcodes to indicate possible equality. For further guidance see Taligent's Java Cookbook
Override readObject and WriteObject if a Serializable class relies on any state that could differe across processes, including, in particular, hadhCodes.
Rationale: Otherwise, objects of the class will not transport properly.
If you think that clone() may be called in a class you write, then explicitly define it (and declare the class to implement Cloneable).
Rationale: The default shallow-copy version of clone might not do what you want.
Always document the fact that a method invokes wait
Rationale: Clients may need to take special actions to avoid nested monitor calls.
Whenever reasonable, define a default (no-argument) constructor so objects can be created via Class.newInstance().
Rationale: This allows classes of types unknown at compile time to be dynamically loaded and instantiated (as is done for example when loading unknown Applets from html pages).
Prefer abstract methods in base classes to those with default no-op implementations. (Also, if there is a common default implementation, consider instead writing it as a protected method so that subclass authors can just write a one-line implementation to call the default.)
Rationale: The Java compiler will force subclass authors to implement abstract methods, avoiding problems occurring when they forget to do so but should have.
Use method equals instead of operator == when comparing objects. In particular, do not use == to compare Strings.
Rationale: If someone defined an equals method to compare objects, then they want you to use it. Otherwise, the default implementation of Object.equals is just to use ==.
Always embed wait statements in while loops that re-wait if the condition being waited for does not hold.
Rationale: When a wait wakes up, it does not know if the condition it is waiting for is true or not.
Use notifyAll instead of notify or resume.
Rationale: Classes that use only notify can normally only support at most one kind of wait condition across all methods in the class and all possible subclasses. And unguarded suspends/resumes are even more fragile.
Declare a local variable only at that point in the code where you know what its initial value should be.
Rationale: Minimizes bad assumptions about values of variables.
Declare and initialize a new local variable rather than reusing (reassigning) an existing one whose value happens to no longer be used at that program point.
Rationale: Minimizes bad assumptions about values of variables.
Assign null to any reference variable that is no longer being used. (This includes, especially, elements of arrays.)
Rationale: Enables garbage collection.
Avoid assignments (``='') inside if and while conditions.
Rationale: There are almost always typos. The java compiler catches cases where ``='' should have been ``=='' except when the variable is a boolean.
Document cases where the return value of a called method is ignored.
Rationale: These are typically errors. If it is by intention, make the intent clear.
Ensure that there is ultimately a catch for all unchecked exceptions that can be dealt with.
Rationale: Java allows you to not bother declaring or catching some common easily-handlable exceptions, for example java.util.NoSuchElementException. Declare and catch them anyway.
Embed casts in conditionals. For example:
        C cx = null;
        if (x instanceof C) cx = (C)x;
        else evasiveAction();
   

Rationale: This forces you to consider what to do if the object is not an instance of the intended class rather than just generating a ClassCastException.

Document fragile constructions used solely for the sake of optimization.
Rationale: See Jonathan Hardwick's Java Optimization pages.
Do not require 100% conformance to rules of thumb such as the ones listed here!
Rationale: Java allows you program in ways that do not conform to these rules for good reason. Sometimes they provide the only reasonable ways to implement things. And some of these rules make programs less efficient than they might otherwise be, so are meant to be concientiously broken when performance is an issue.

Related Documents

For some other standards and style guides, see


patrick@fnal.gov

Last modified: March 2, 1998