The new Java 2 Platform Standard Edition 5.0 (the developer version number is 1.5 and the code name is "Tiger") provides many new features, among them is the ability to annotate Java program elements and to create custom annotation types. Development and deployment tools can then read annotated data (also known as metadata) and process it in some fashion.
Previous versions of Java provided a limited and ad-hoc mechanism for annotating code through JavaDoc comments and keyword modifiers. Tools such as XDoclet provide a slightly more sophisticated, yet non-standard annotation syntax, which piggybacks on top of JavaDoc. But now, with Java 1.5, annotations are a typed part of the Java language and allow for both runtime and compile-time processing of annotation data.
What Are Annotations?
In short, annotations are metadata or data about data. Annotations are said to annotate a Java element. An annotation indicates that the declared element should be processed in some special way by a compiler, development tool, deployment tool, or during runtime.
Older versions of Java have rough-and-ready annotation functionality. A good example is the @deprecated JavaDoc tag. The @deprecated tag is used for more than sheer documentation purposes. This tag has no effect on the code it describes, but causes the compiler to produce warnings if any other code references the tagged element. JavaDoc does not seem to be the proper place for this type of metadata, but there was no other annotation facility in previous versions of Java. With Java 1.5, finally, annotations are a typed part of the language and the version even comes with some with pre-built annotations, one of which can be used to mark a class as deprecated (I'll cover this later).
The code below shows how you can declare a method that uses an annotation. It is one of Java 1.5's built-in annotation types:
class Child extends Parent {
@Overrides
public void doWork() {
//do something
}
}
In the code above, note that the annotation starts with an "at" (@) sign. This annotation takes no parameters and is merely used to mark a method for some purpose. It is therefore called a marker annotation. There are also normal annotations and single member annotations (more on these later).
Annotations types are blueprints for annotations, similar to how a class is the blueprint for an object. You can create your own custom annotations by defining annotation types.
The code below shows the declaration of a normal annotation type:
public @interface MyAnnotationType {
int someValue();
String someOtherValue();
}
Annotations can be analyzed statically before and during compile time. Annotations will likely be used before compile time mainly to generate supporting classes or configuration files. For example, a code generator (XDoclet, for example) can use annotation data in an EJB implementation class to generate EJB interfaces and deployment descriptors for you, reducing both your effort and the error rate. The average developer will probably not be writing code-generation tools, so these annotation types are likely to be used out-of-the-box rather than authored anew.
Author's Note:
For an overview of its new features and a brief guide to downloading and installing Java 5.0, refer to Laurence Moroney's article, "Tiger Stripes: Get Ready to Purr over J2SE 5.0."
The annotations specification was laid out in JSR-175; download it from http://www.jcp.org/en/jsr/detail?id=175.
C# has a similar mechanism for doing annotations called custom attributes. To learn more about C# custom attributes, refer to Pierre Nallet's article, "Get Personal with C# Custom Attributes."
Annotations will also be used for compile-time checking such as to produce warnings and errors for different failure scenarios. An example of an annotation that is used at compile time is the new @Deprecated annotation, which acts the same as the old @deprecated JavaDoc tag. Of course, a compiler has to know how to interpret annotation data that is meant to produce compile-time warnings, so again, annotations that do things at compile time will likely be used frequently, but rarely written by average developers.
Annotations can be useful at runtime as well. Using annotations you could mark code to behave in a particular way whenever it is called. For example, you could mark some methods with a @prelog annotation. Then at runtime, you could analyze the methods that you're calling and print a particular log message before you begin executing code for that method. One way to achieve this would be through the use of the updated Java 1.5 reflection API. The reflection API now provides access to runtime-accessible annotation data.
< Another way to use annotations at runtime is to use Aspect-Oriented Programming (AOP). AOP uses pointcuts—sets of points configured to executed aspects. You could define a pointcut that will execute an aspect for an annotated method. My guess is that developers would be more likely to write their own runtime annotation types than they would annotation types used for code generation and compile-time checking. Still, writing and understanding the code that accesses the annotations (the annotation consumer) at runtime is fairly advanced.
Annotating Code
Annotations fall into three categories: normal annotations, single member annotations, and marker annotations (see Table 1). Normal and single member annotations can take member values as arguments when you annotate your code.
Category Example
Normal Annotations—Annotations that take multiple arguments. The syntax for these annotations provides the ability to pass in data for all the members defined in an annotation type. @MyNormalAnnotation(mem1="val1", mem2="val2") public void someMethod() { ... }
Single Member Annotations—An annotation that only takes a single argument has a more compact syntax. You don't need to provide the member name. @MySingleMemberAnnotation("a single value") public class SomeClass { ... }
Marker Annotations—These annotations take no parameters. They are used to mark a Java element to be processed in a particular way. @Deprecated public void doWork() { ... }
Any Java declaration can be marked with an annotation. That is, an annotation can be used on a: package, class, interface, field, method, parameter, constructor, enum (newly available in Java 1.5), or local variable. An annotation can even annotate another annotation. Such annotations are called meta-annotations.
This code below shows an annotated class, constructor, field, and method.
@ClassLevelAnnotation(arg1="val1", arg2={"arg2.val1","arg2.val2"})
public class AnnotationExample {
@FieldLevelAnnotation()
public String field;
@CtorLevelAnnotation()
public AnnotationsTest() {
// code
}
@MethodLevelAnnotationA("val")
@MethodLevelAnnotationB(arg1="val1",arg2="val2")
public void someMethod(String string) {
// code
}
}
As you can see in the ClassLevelAnnotation, annotations may also take arrays of values. When creating an annotation that requires an array you must surround the array of parameters with brackets ( { … } ) and you must comma-separate each array parameter.
Annotation types may define default values for some members. Every member-value pair that does not have a default value must be supplied when you create an annotation.
The annotation-type declaration below uses meta-annotations, which provide information on how the annotation type can be used:
@Retention(RUNTIME)
@Target(METHOD)
public @interface MyAnnotationType {
String value;
}
This code shows how to use meta-annotations when declaring your own annotation type. I will cover the specifics on declaring annotation types and meta-annotation types later, but for now, just know that you can use meta-annotations to annotate other annotations.
Packages annotations are also allowed, but because packages are not explicitly declared in Java, package annotations must be declared in a source file called package-info.java in the directory containing the source files for the package. This file should contain only a package declaration, preceded by any annotations that should apply to the package. This java file must not contain an actual class definition (which would be illegal anyways, because package-info is not a legal identifier).
Built-in Annotations
Java 1.5 comes packaged with seven pre-built annotations. I will describe these built-in annotations in this section, borrowing much of the wording directly from the Java 5 documentation.
java.lang.Override
@Target(value=METHOD)
@Retention(value=SOURCE)
public @interface Override
This annotation is used to indicate that a method declaration is intended to override a method declaration in a superclass. If a method is annotated with this annotation type but does not override a superclass method, compilers are required to generate an error message.
This annotation is useful in avoiding the situation where you think you are overriding a method, but you misspell the method name in the child class. The code in the method you are trying to override in the parent class will be used because the name is misspelled in the child class. This is usually a difficult bug to track down because no compiler or runtime error is thrown. But marking a method with the Override annotation would help you realize this type of problem at compile time rather than through arduous debugging.
java.lang.Deprecated
@Documented
@Retention(value=RUNTIME)
public @interface Deprecated
A program element annotated @Deprecated is one that programmers are discouraged from using, typically because it is dangerous or because a better alternative exists. Using this annotation, compilers warn when a deprecated program element is used or overridden in non-deprecated code.
java.lang.SuppressWarning
@Target(value={TYPE,FIELD,METHOD,PARAMETER,CONSTRUCTOR,LOCAL_VARIABLE})
@Retention(value=SOURCE)
public @interface SuppressWarnings
This annotation indicates that the named compiler warnings should be suppressed in the annotated element (and in all program elements contained in the annotated element). Note that the set of warnings suppressed in a given element is a superset of the warnings suppressed in all containing elements. For example, if you annotate a class to suppress one warning and annotate a method to suppress another, both warnings will be suppressed in the method.
java.lang.annotation.Documented
@Documented
@Retention(value=RUNTIME)
@Target(value=ANNOTATION_TYPE)
public @interface Documented
Annotations with a type declaration are to be documented by javadoc and similar tools by default. It should be used to annotate the declarations of types whose annotations affect the use of annotated elements by their clients. If a type declaration is annotated with @Documented, its annotations become part of the public API of the annotated elements.
java.lang.annotation.Inherited
@Documented
@Retention(value=RUNTIME)
@Target(value=ANNOTATION_TYPE)
public @interface Inherited
This indicates that an annotation type is automatically inherited. If an inherited meta-annotation is present on an annotation type declaration, and the user queries the annotation type on a class declaration, and the class declaration has no annotation for this type, then the class's superclass will automatically be queried for the annotation type. This process will be repeated until an annotation for this type is found or the top of the class hierarchy (Object) is reached. If no superclass has an annotation for this type, then the query will indicate that the class in question has no such annotation.
Note that this meta-annotation type has no effect if the annotated type is used for anything other than a class. Also, this meta-annotation causes annotations to be inherited only from superclasses; annotations on implemented interfaces have no effect.
java.lang.annotation.Retention
@Documented
@Retention(value=RUNTIME)
@Target(value=ANNOTATION_TYPE)
public @interface Retention
Retention takes a single value of the enumerated type RetentionPolicy, which informs the compiler of its policy for retaining annotations in memory. There are three RetentionPolicy enumerated values:
SOURCE—Annotations are to be discarded by the compiler.
CLASS—Annotations are to be recorded in the class file by the compiler but need not be retained by the VM at runtime. This is the default behavior.
RUNTIME—Annotations are to be recorded in the class file by the compiler and retained by the VM at runtime, so they may be read reflectively.
If no Retention annotation is present on an annotation type declaration, the retention policy defaults to RetentionPolicy.CLASS. By default, the annotation data is not available in the JVM in order to reduce overhead.
java.lang.annotation.Target
@Documented
@Retention(value=RUNTIME)
@Target(value=ANNOTATION_TYPE)
public @interface Target
@Target indicates the kinds of program element to which an annotation type is applicable. It takes a single enumerated parameter of type ElementType. The enumerated values are as follows:
TYPE (class, interface or enum declaration)
FIELD (includes enum constants)
METHOD
PARAMETER
CONSTRUCTOR
LOCAL_VARIABLE
ANNOTATION_TYPE
PACKAGE
If a @Target meta-annotation is not present on an annotation type declaration, the declared type may be used on any program element. If such a meta-annotation is present, the compiler will enforce the specified usage restriction.
Declaring Annotation Types
Now that you've learned a little about the annotations that come packaged with Java 1.5, you can move on to declaring your own annotation types.
Here is a sample annotation type:
public @interface MyAnnotationType {
int someValue();
String someOtherValue();
String yesSomeOtherValue() default "[blank]";
}
As you can see, annotation types are declared similarly to interfaces. In fact they use the interface keyword prepended with an @ sign. Just like an interface, annotations have method declarations, each of which defines a member of the annotation. These methods may not have any parameters. As shown in the example, default values are specified after the parenthesis on a method declaration.
At first, it seems a little odd to specify the members of an annotation using method syntax, because when you declare a Java element and annotate it (e.g. when you annotate a class), you may pass values into the annotation. But you are not the consumer of the annotation at this point; you are merely constructing an instance of the annotation. Think of it as calling a constructor on an implicit Java class that you didn't have to write.
The annotation consumers are the development tools, the compiler, or a runtime library that accesses the annotation data you created when you annotated your Java code. After you've created the annotation, annotation consumers may call the methods on the annotation interface in order to get the annotation values. The return types of the methods represent the type of data that a consumer of the annotation would get back.
There are a few restrictions when defining annotation types.
Annotations cannot extend other annotations, except for the java.lang.annotation.Annotation marker interface they inherently extend.
Method return types must be: primitive types, String, Class, enum types, annotation types, or arrays of the preceding types.
Annotations can't have a throws clause.
Self reference is not allowed (e.g. AnnotationA contains a member of type AnnotationA), nor circular reference (e.g. AnnotationA contains a member of type AnnotationB and vice versa).
Single member annotations must define only a single method called value in the annotation type. So long as you follow this construct, you can use the condensed single-member syntax when creating an annotation of this type.
A single member annotation can be defined as follows:
public @interface Copyright {
String value();
}
You can also create meta-annotations, as we have already seen. A meta-annotation is any annotation that can be used to annotate other annotations. For example, all of the build-in annotations utilize meta-annotations and some of them are meta-annotations themselves.
Here is the declaration for the @ Inherited meta-annotation type:
@Documented
@Retention(value=RUNTIME)
@Target(value=ANNOTATION_TYPE)
public @interface Inherited
The @Inherited annotation uses three other meta-annotations and is a meta-annotation itself. What makes it a meta-annotation? Any annotation whose @Target annotation member includes ANNOTATION_TYPE can be used as a meta-annotation.
Reading Annotations
Now that you know how to declare your own annotation types and annotate Java elements, you need to know how to access the annotation data you specified. Annotation consumers are software that read annotation data and do something useful with it. Annotation consumers fall into three groups:
General tools are programs that can analyze source code and do something useful with it. For example compilers and documentation generators are both considered general tools. General tools do not load annotated classes or annotation interfaces into the virtual machine.
Specific tools are also programs that can analyze source code without loading annotated classes, but they need to load annotation interfaces into the virtual machine. An example of this is a Stub generator.
Introspectors are programs that can query their own annotations (the ones with a RUNTIME retention policy). Introspectors will load both annotated classes and annotation interfaces into the virtual machine.
Listing 1 is an example of how you can access your code during runtime using the reflection API.
As I mentioned previously, I think that most developers will be users of annotations, but few will have to write code that consumes annotations.
If you are a general or specific tools developer, you may want to look into a few APIs that will help you read annotations from source files in order to use them for some type of static preprocessing. The new Doclet API (com.sun.javadoc) has support for reading annotations. It doesn’t currently support all of the features of annotations, but it may suffice as a library for writing your tool. Another thing to keep an eye on is the newly submitted JSR-269 (Pluggable Annotation Processing API), which will provide a generic API that allows developers to read annotation data using general and specific tools.
If you are developing an introspector, you can use the new reflection API to access your annotations, though I would suggest looking into AOP as a means to interface with annotated methods. I think there is a lot of room for exploration here. The combinations of aspects and annotations can yield a very powerful introspection tool.
Javid Jamae consults for Valtech, a global consulting group specializing in delivering advanced technology solutions. Valtech endeavors to help its customers through its global delivery model to create and/or maintain an affordable competitive advantage.
(http://www.devx.com/Java/Article/27235/1954?pf=true)
Wednesday, May 17, 2006
Peeking Inside the Box: Attribute-Oriented Programming with Java 1.5 by Don Schwarz
Don Schwarz is a Java developer for a large investment bank who specializes in metaprogramming and language integration.
I recently spent an afternoon at the London Science Museum watching steam engines, pumps, turbines, and other antique machines operate. Some were so simple that their behavior was obvious, but most had sections cut away and replaced with glass windows so their inner workings could be observed. As I walked through the exhibits, I couldn't help but wonder how the craftsmen and maintenance crews of those machines were able to do their jobs without the cut-away models and diagrams that accompanied the exhibits here. Software engineers have it easy!
To a seasoned Java developer with access to source code, any program can look like the transparent models at the museum. Tools such as thread dumps, method call tracing, breakpoints, and profiling statistics can provide insight into what a program is doing right now, has done in the past, and may do in the future. But in a production environment, things aren't always so obvious. These tools are not typically available, or at the very least, they are usable only by trained developers. Support teams and end users also need to be aware of what an application is doing at any given time.
To fill this void, we've invented simpler alternatives such as log files (typically for server processes) and status bars (for GUI applications). However, because these tools must capture and report only a very small subset of the information available to them and often must present this information in a way that is easy to understand, they tend to be programmed explicitly into the application. This code is intertwined with the application's business logic in such a way that developers must "work around it" when trying to debug or understand core functionality, yet remember to update this code whenever that functionality changes. What we really want to do is to centralize the status-reporting logic in a single place, and manage the individual status messages as metadata.
In this article, I will consider the case of a status-bar component embedded in a GUI application. I will explore a number of different ways to implement this status reporter, starting with the traditional hard-coded idiom. Along the way, I will introduce and discuss a number of new features in Java 1.5, including annotations and run-time bytecode instrumentation.
The StatusManager
My primary goal is to create a JStatusBar Swing component that can be embedded in our GUI application. Figure 1 shows what the finished status bar looks like in a simple JFrame.
Figure 1. Our dynamically generated status bar
Since I do not wish to reference any GUI components directly from our business logic, I'll also create a StatusManager to act as an entry point for status updates. The actual notification will be delegated out to a StatusState object so that later this can be extended to support multiple concurrent threads. Figure 2 shows this arrangement
Figure 2. StatusManager and JStatusBar
Now I need to write code which that call the methods on StatusManager to report on the progress of the application. These method calls are typically scattered throughout the code in try-finally blocks, often one per method.
public void connectToDB (String url) {
StatusManager.push("Connecting to database");
try {
...
} finally {
StatusManager.pop();
}
}
The code in the 01_original directory in the peekinginside-pt1.tar.gz sample code (see References below) exhibits this traditional approach.
This code is functional, but after duplicating it dozens or even hundreds of times throughout your codebase, it can begin to look a bit messy. In addition, what if I want to access these messages in some other way? Later in this article, I'll define a user-friendly exception handler that shares the same messages. The problem is that I've hidden the status message in the implementation of our method, rather than on the interface where it belongs.
Attribute-Oriented Programming
What I really want to do is to leave any references to StatusManager out of our code altogether and simply tag the method with our message. I can then use either code-generation or run-time introspection to do the real work. The XDoclet Project refers to this as Attribute-Oriented Programming, and provides a framework which can convert custom Javadoc-like tags into source code.
However, with the inclusion of JSR-175, Java 1.5 has provided a more structured format for including these attributes inside of real code. The attributes are called "annotations" and they can be used to provide metadata for class, method, field, or variable definitions. They must be declared explicitly, and provide a sequence of name-value pairs that can contain any constant value (including primitives, strings, enumerations, and classes).
Annotations
To hold our status message, I'll want to define a new annotation that contains a string value. Annotation definitions look a lot like interface definitions, but the keyword @interface is used instead of interface and only methods are supported (though they act more like fields).
public @interface Status {
String value();
}
Like an interface, I will put the @interface in a file named Status.java, and import it into any other files that need to reference it.
value may seem like a strange name for our field here. Something like message might be more appropriate; however, value has special meaning to Java. This allows us to declare annotations as @Status("...") rather than @Status(value="..."), as a shortcut.
I can now define my method as follows:
@Status("Connecting to database")
public void connectToDB (String url) {
...
}
Note that to compile this code I needed to use the -source 1.5 option. If you're using Ant (as our code samples do) rather than building a javac command line directly, you'll also need Ant 1.6.1.
In addition to classes, methods, fields, and variables, annotations can also be used to provide metadata for other annotations. In particular, Java comes with a few annotations that can be used to customize how your annotation works. Let's redefine our annotation as follows:
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.SOURCE)
public @interface Status {
String value();
}
The @Target annotation defines what the @Status annotation can reference. Ideally, I would like to mark up arbitrary blocks of code, but the only options are methods, fields, classes, local variables, parameters, and other annotations. I'm only interested in code, so I choose METHOD.
The @Retention annotation allows us to specify when Java is free to discard this information. It can be either SOURCE (discard when compiling), CLASS (discard at class load time), or RUNTIME (do not discard). Let's start with SOURCE, but I will need to upgrade it later.
Instrumenting Source Code
Now that my messages are encoded in metadata, I'll need some code to notify the status listeners. Let's assume for the moment that I continue to store my connectToDB method in source code control without any references to StatusManager. However, before compiling the class, I want to add in the necessary calls. That is, I want to insert the try-finally statements and the push/pop calls automatically.
The XDoclet framework is a source code generation engine for Java that uses annotations similar to those described above, but stored in Java source code comments. XDoclet works very well for generating entire Java classes, configuration files, or other build artifacts, but does not support the modification of existing Java classes. This limits its usefulness for instrumentation. Instead, I could use a parser tool like JavaCC or ANTLR (which comes with a grammar for parsing Java source code), but that requires a fair amount of effort.
There seems to be no good tool available for doing source code instrumentation of Java code. There may be a market for such a tool, but as you will see in the rest of this article, bytecode instrumentation can be a much more powerful technique.
Instrumenting Bytecode
Rather than instrumenting source code and then compiling it, I could compile the original source code and then instrument the bytecode that is produced. Depending on the exact transformation required, this can be either much easier or much more difficult than source code instrumentation. The main advantage of bytecode instrumentation is that the code can be modified at run time, without having a compiler available.
Although Java's bytecode format is relatively simple, I will certainly want to use a Java library to do the parsing and generation of the bytecode (e.g., to insulate us from future changes in the Java class file format). I chose to use Jakarta's Byte Code Engineering Library (BCEL), but I could just as easily have picked CGLIB, ASM, or SERP.
Since I will be instrumenting bytecode in a number of different ways, I'll begin by declaring a generic interface for instrumentation. This will act as a simple framework for doing annotation-based instrumentation. This framework will support the transformation of classes and methods, based on annotations, so the interface will look something like this:
public interface Instrumentor
{
public void instrumentClass (ClassGen classGen,
Annotation a);
public void instrumentMethod (ClassGen classGen,
MethodGen methodGen,
Annotation a);
}
ClassGen and MethodGen are BCEL classes that implement the Builder pattern. That is, they provide methods for mutating an otherwise immutable object, and for transforming between the mutable and non-mutable representations.
Now I will need to write an implementation for this interface that replaces @Status annotations with the appropriate calls to StatusManager. As described previously, I want to wrap these calls in a try-finally block. Note that for this to work, the annotations that are used must be tagged with @Retention(RetentionPolicy.CLASS), which instructs the Java compiler not to discard the annotations while compiling. Since I declared @Status as @Retention(RetentionPolicy.SOURCE) earlier, I need to upgrade it.
As it turns out, in this case, instrumenting bytecode is significantly more difficult than instrumenting source code. The reason is that try-finally is a concept that exists in source code only! The Java compiler transforms try-finally blocks into a series of try-catch blocks and inserts calls to the finally block before every return. Thus, I will need to do something similar in order to add a try-finally block to existing bytecode.
This is the bytecode that represents an ordinary method call, flanked by StatusManager updates.
0: ldc #2; //String message
2: invokestatic #3; //Method StatusManager.push:(LString;)V
5: invokestatic #4; //Method doSomething:()V
8: invokestatic #5; //Method StatusManager.pop:()V
11: return
This is the same method call, but in a try-finally block, so that StatusManager.pop() is called even if an exception is thrown.
0: ldc #2; //String message
2: invokestatic #3; //Method StatusManager.push:(LString;)V
5: invokestatic #4; //Method doSomething:()V
8: invokestatic #5; //Method StatusManager.pop:()V
11: goto 20
14: astore_0
15: invokestatic #5; //Method StatusManager.pop:()V
18: aload_0
19: athrow
20: return
Exception table:
from to target type
5 8 14 any
14 15 14 any
As you can see, I need to duplicate some instructions and add several jumps and exception table records just to implement a single try-finally. Luckily, BCEL's InstructionList class makes this fairly easy.
Instrumenting Bytecode At Compile Time
Now that I have an interface for modifying classes based on annotations and a concrete implementation of this interface, the last step is to write the actual framework that will call it. I'm actually going to write a few of these frameworks, starting with one that instruments all classes at a compile time. Since this is going to happen as part of my build process, I've decided to define an Ant task for it. The declaration of the instrumentation target in my build.xml file should look something like this:
To provide an implementation of this task, I need to define a class that realizes the org.apache.tools.ant.Task interface. Attributes and sub-elements of our task are passed in through set and add method calls. The execute method is called to implement the work of the task -- in this case, to instrument the class files given in the specified.
public class InstrumentTask extends Task {
...
public void setClass (String className) { ... }
public void addFileSet (FileSet fileSet) { ... }
public void execute () throws BuildException {
Instrumentor inst = getInstrumentor();
try {
DirectoryScanner ds =
fileSet.getDirectoryScanner(project);
// Java 1.5 "for" syntax
for (String file : ds.getIncludedFiles()) {
instrumentFile(inst, file);
}
} catch (Exception ex) {
throw new BuildException(ex);
}
}
...
}
The one problem with using BCEL for this purpose is that as of version 5.1, it does not support parsing annotations. I could load the classes that we're instrumenting and use reflection to view the annotations. However, then I would have had to use RetentionPolicy.RUNTIME instead of RetentionPolicy.CLASS. I'd also be executing any static initializers in those classes, which may load native libraries or introduce other dependencies. Luckily, BCEL provides a plugin mechanism that allows clients to parse bytecode attributes that it does not natively support. I've written my own implementation of the AttributeReader interface that knows how to parse the RuntimeVisibleAnnotations and RuntimeInvisibleAnnotations attributes that are inserted into bytecode when annotations are present. Future versions of BCEL should include this functionality without the need for a plugin.
This compile time bytecode instrumentation approach is shown in the directory code/02_compiletime of the sample code.
There are a number of disadvantages to this approach, however. For one thing, I had to add an additional step to my build process. I also cannot turn the instrumentation on or off based on command-line settings or other information that is not available at compile time. If both instrumented and non-instrumented code needs to be run in a production setting, two separate .jars will need to be built and the decision of which to use must be all or nothing.
Instrumenting Bytecode at Class Loading Time
A better solution would be to delay the instrumentation of our bytecode until after it is loaded from the disk. This way, the instrumented bytecode does not need to be stored. The start-up performance of our application may suffer, but the trade-off is that you can control what happens based on system properties, or other runtime configuration data.
Prior to Java 1.5, it was possible to do this type of class-file manipulation with a custom class loader. However, the new java.lang.instrument package added in Java 1.5 provides a few additional tools. In particular, it defines the concept of a ClassFileTransformer, which can be used to instrument a class during the standard loading process.
To register our ClassFileTransformer at the appropriate time (before any of our classes are loaded), I'll need to define a premain method. Java will call this before the main class is even loaded, and it is passed a reference to an Instrumentation object. I will also need to add a -javaagent option to the command line to tell Java about our premain method with Java. This argument takes the full name of your agent class (which contains the premain method) and an arbitrary string argument. In this case, we'll pass the full name of our Instrumentor class as the argument (this should all be on one line):
-javaagent:boxpeeking.instrument.InstrumentorAdaptor=
boxpeeking.status.instrument.StatusInstrumentor
Now that I've arranged for a callback that occurs before any annotated classes are loaded, and I have a reference to the Instrumentation object, I can register our ClassFileTransformer.
public static void premain (String className,
Instrumentation i)
throws ClassNotFoundException,
InstantiationException,
IllegalAccessException
{
Class instClass = Class.forName(className);
Instrumentor inst = (Instrumentor)instClass.newInstance();
i.addTransformer(new InstrumentorAdaptor(inst));
}
The adaptor that is registered will need to act as a bridge between the Instrumentor interface given above and Java's ClassFileTransformer interface.
public class InstrumentorAdaptor
implements ClassFileTransformer
{
public byte[] transform (ClassLoader cl,
String className,
Class classBeingRedefined,
ProtectionDomain protectionDomain,
byte[] classfileBuffer)
{
try {
ClassParser cp =
new ClassParser(
new ByteArrayInputStream(classfileBuffer),
className + ".java");
JavaClass jc = cp.parse();
ClassGen cg = new ClassGen(jc);
for (Annotation an :
getAnnotations(jc.getAttributes())) {
instrumentor.instrumentClass(cg, an);
}
for (org.apache.bcel.classfile.Method m :
cg.getMethods()) {
for (Annotation an :
getAnnotations(m.getAttributes())) {
ConstantPoolGen cpg =
cg.getConstantPool();
MethodGen mg =
new MethodGen(m, className, cpg);
instrumentor.instrumentMethod(cg, mg, an);
mg.setMaxStack();
mg.setMaxLocals();
cg.replaceMethod(m, mg.getMethod());
}
}
JavaClass jcNew = cg.getJavaClass();
return jcNew.getBytes();
} catch (Exception ex) {
throw new RuntimeException("instrumenting " +
className, ex);
}
}
...
}
This approach of instrumenting bytecode at startup is shown in the example code's /code/03_startup directory.
Conclusion
In this article, I have replaced a hard-coded solution with one that uses metaprogramming based on annotations and instrumentation. Although I've eliminated the need for any extra steps in our build process, my solution still has a number of limitations. In the next installment, I will explore a completely different implementation that uses thread sampling, and then combine these two techniques to create a solution that gives the best features of each. I will also discuss a number of additional requirements, including a progress bar and dynamic status messages.
References
Example source code for this article
"JSR-175 Public Draft Specification: A Program Annotation Facility for the JavaTM Programming Language"
BCEL web site: jakarta.apache.org/bcel
XDoclet web site: http://xdoclet.sourceforge.net
"J2SE 1.5 in a Nutshell"
"Declarative Programming in Java"
I recently spent an afternoon at the London Science Museum watching steam engines, pumps, turbines, and other antique machines operate. Some were so simple that their behavior was obvious, but most had sections cut away and replaced with glass windows so their inner workings could be observed. As I walked through the exhibits, I couldn't help but wonder how the craftsmen and maintenance crews of those machines were able to do their jobs without the cut-away models and diagrams that accompanied the exhibits here. Software engineers have it easy!
To a seasoned Java developer with access to source code, any program can look like the transparent models at the museum. Tools such as thread dumps, method call tracing, breakpoints, and profiling statistics can provide insight into what a program is doing right now, has done in the past, and may do in the future. But in a production environment, things aren't always so obvious. These tools are not typically available, or at the very least, they are usable only by trained developers. Support teams and end users also need to be aware of what an application is doing at any given time.
To fill this void, we've invented simpler alternatives such as log files (typically for server processes) and status bars (for GUI applications). However, because these tools must capture and report only a very small subset of the information available to them and often must present this information in a way that is easy to understand, they tend to be programmed explicitly into the application. This code is intertwined with the application's business logic in such a way that developers must "work around it" when trying to debug or understand core functionality, yet remember to update this code whenever that functionality changes. What we really want to do is to centralize the status-reporting logic in a single place, and manage the individual status messages as metadata.
In this article, I will consider the case of a status-bar component embedded in a GUI application. I will explore a number of different ways to implement this status reporter, starting with the traditional hard-coded idiom. Along the way, I will introduce and discuss a number of new features in Java 1.5, including annotations and run-time bytecode instrumentation.
The StatusManager
My primary goal is to create a JStatusBar Swing component that can be embedded in our GUI application. Figure 1 shows what the finished status bar looks like in a simple JFrame.
Figure 1. Our dynamically generated status bar
Since I do not wish to reference any GUI components directly from our business logic, I'll also create a StatusManager to act as an entry point for status updates. The actual notification will be delegated out to a StatusState object so that later this can be extended to support multiple concurrent threads. Figure 2 shows this arrangement
Figure 2. StatusManager and JStatusBar
Now I need to write code which that call the methods on StatusManager to report on the progress of the application. These method calls are typically scattered throughout the code in try-finally blocks, often one per method.
public void connectToDB (String url) {
StatusManager.push("Connecting to database");
try {
...
} finally {
StatusManager.pop();
}
}
The code in the 01_original directory in the peekinginside-pt1.tar.gz sample code (see References below) exhibits this traditional approach.
This code is functional, but after duplicating it dozens or even hundreds of times throughout your codebase, it can begin to look a bit messy. In addition, what if I want to access these messages in some other way? Later in this article, I'll define a user-friendly exception handler that shares the same messages. The problem is that I've hidden the status message in the implementation of our method, rather than on the interface where it belongs.
Attribute-Oriented Programming
What I really want to do is to leave any references to StatusManager out of our code altogether and simply tag the method with our message. I can then use either code-generation or run-time introspection to do the real work. The XDoclet Project refers to this as Attribute-Oriented Programming, and provides a framework which can convert custom Javadoc-like tags into source code.
However, with the inclusion of JSR-175, Java 1.5 has provided a more structured format for including these attributes inside of real code. The attributes are called "annotations" and they can be used to provide metadata for class, method, field, or variable definitions. They must be declared explicitly, and provide a sequence of name-value pairs that can contain any constant value (including primitives, strings, enumerations, and classes).
Annotations
To hold our status message, I'll want to define a new annotation that contains a string value. Annotation definitions look a lot like interface definitions, but the keyword @interface is used instead of interface and only methods are supported (though they act more like fields).
public @interface Status {
String value();
}
Like an interface, I will put the @interface in a file named Status.java, and import it into any other files that need to reference it.
value may seem like a strange name for our field here. Something like message might be more appropriate; however, value has special meaning to Java. This allows us to declare annotations as @Status("...") rather than @Status(value="..."), as a shortcut.
I can now define my method as follows:
@Status("Connecting to database")
public void connectToDB (String url) {
...
}
Note that to compile this code I needed to use the -source 1.5 option. If you're using Ant (as our code samples do) rather than building a javac command line directly, you'll also need Ant 1.6.1.
In addition to classes, methods, fields, and variables, annotations can also be used to provide metadata for other annotations. In particular, Java comes with a few annotations that can be used to customize how your annotation works. Let's redefine our annotation as follows:
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.SOURCE)
public @interface Status {
String value();
}
The @Target annotation defines what the @Status annotation can reference. Ideally, I would like to mark up arbitrary blocks of code, but the only options are methods, fields, classes, local variables, parameters, and other annotations. I'm only interested in code, so I choose METHOD.
The @Retention annotation allows us to specify when Java is free to discard this information. It can be either SOURCE (discard when compiling), CLASS (discard at class load time), or RUNTIME (do not discard). Let's start with SOURCE, but I will need to upgrade it later.
Instrumenting Source Code
Now that my messages are encoded in metadata, I'll need some code to notify the status listeners. Let's assume for the moment that I continue to store my connectToDB method in source code control without any references to StatusManager. However, before compiling the class, I want to add in the necessary calls. That is, I want to insert the try-finally statements and the push/pop calls automatically.
The XDoclet framework is a source code generation engine for Java that uses annotations similar to those described above, but stored in Java source code comments. XDoclet works very well for generating entire Java classes, configuration files, or other build artifacts, but does not support the modification of existing Java classes. This limits its usefulness for instrumentation. Instead, I could use a parser tool like JavaCC or ANTLR (which comes with a grammar for parsing Java source code), but that requires a fair amount of effort.
There seems to be no good tool available for doing source code instrumentation of Java code. There may be a market for such a tool, but as you will see in the rest of this article, bytecode instrumentation can be a much more powerful technique.
Instrumenting Bytecode
Rather than instrumenting source code and then compiling it, I could compile the original source code and then instrument the bytecode that is produced. Depending on the exact transformation required, this can be either much easier or much more difficult than source code instrumentation. The main advantage of bytecode instrumentation is that the code can be modified at run time, without having a compiler available.
Although Java's bytecode format is relatively simple, I will certainly want to use a Java library to do the parsing and generation of the bytecode (e.g., to insulate us from future changes in the Java class file format). I chose to use Jakarta's Byte Code Engineering Library (BCEL), but I could just as easily have picked CGLIB, ASM, or SERP.
Since I will be instrumenting bytecode in a number of different ways, I'll begin by declaring a generic interface for instrumentation. This will act as a simple framework for doing annotation-based instrumentation. This framework will support the transformation of classes and methods, based on annotations, so the interface will look something like this:
public interface Instrumentor
{
public void instrumentClass (ClassGen classGen,
Annotation a);
public void instrumentMethod (ClassGen classGen,
MethodGen methodGen,
Annotation a);
}
ClassGen and MethodGen are BCEL classes that implement the Builder pattern. That is, they provide methods for mutating an otherwise immutable object, and for transforming between the mutable and non-mutable representations.
Now I will need to write an implementation for this interface that replaces @Status annotations with the appropriate calls to StatusManager. As described previously, I want to wrap these calls in a try-finally block. Note that for this to work, the annotations that are used must be tagged with @Retention(RetentionPolicy.CLASS), which instructs the Java compiler not to discard the annotations while compiling. Since I declared @Status as @Retention(RetentionPolicy.SOURCE) earlier, I need to upgrade it.
As it turns out, in this case, instrumenting bytecode is significantly more difficult than instrumenting source code. The reason is that try-finally is a concept that exists in source code only! The Java compiler transforms try-finally blocks into a series of try-catch blocks and inserts calls to the finally block before every return. Thus, I will need to do something similar in order to add a try-finally block to existing bytecode.
This is the bytecode that represents an ordinary method call, flanked by StatusManager updates.
0: ldc #2; //String message
2: invokestatic #3; //Method StatusManager.push:(LString;)V
5: invokestatic #4; //Method doSomething:()V
8: invokestatic #5; //Method StatusManager.pop:()V
11: return
This is the same method call, but in a try-finally block, so that StatusManager.pop() is called even if an exception is thrown.
0: ldc #2; //String message
2: invokestatic #3; //Method StatusManager.push:(LString;)V
5: invokestatic #4; //Method doSomething:()V
8: invokestatic #5; //Method StatusManager.pop:()V
11: goto 20
14: astore_0
15: invokestatic #5; //Method StatusManager.pop:()V
18: aload_0
19: athrow
20: return
Exception table:
from to target type
5 8 14 any
14 15 14 any
As you can see, I need to duplicate some instructions and add several jumps and exception table records just to implement a single try-finally. Luckily, BCEL's InstructionList class makes this fairly easy.
Instrumenting Bytecode At Compile Time
Now that I have an interface for modifying classes based on annotations and a concrete implementation of this interface, the last step is to write the actual framework that will call it. I'm actually going to write a few of these frameworks, starting with one that instruments all classes at a compile time. Since this is going to happen as part of my build process, I've decided to define an Ant task for it. The declaration of the instrumentation target in my build.xml file should look something like this:
To provide an implementation of this task, I need to define a class that realizes the org.apache.tools.ant.Task interface. Attributes and sub-elements of our task are passed in through set and add method calls. The execute method is called to implement the work of the task -- in this case, to instrument the class files given in the specified
public class InstrumentTask extends Task {
...
public void setClass (String className) { ... }
public void addFileSet (FileSet fileSet) { ... }
public void execute () throws BuildException {
Instrumentor inst = getInstrumentor();
try {
DirectoryScanner ds =
fileSet.getDirectoryScanner(project);
// Java 1.5 "for" syntax
for (String file : ds.getIncludedFiles()) {
instrumentFile(inst, file);
}
} catch (Exception ex) {
throw new BuildException(ex);
}
}
...
}
The one problem with using BCEL for this purpose is that as of version 5.1, it does not support parsing annotations. I could load the classes that we're instrumenting and use reflection to view the annotations. However, then I would have had to use RetentionPolicy.RUNTIME instead of RetentionPolicy.CLASS. I'd also be executing any static initializers in those classes, which may load native libraries or introduce other dependencies. Luckily, BCEL provides a plugin mechanism that allows clients to parse bytecode attributes that it does not natively support. I've written my own implementation of the AttributeReader interface that knows how to parse the RuntimeVisibleAnnotations and RuntimeInvisibleAnnotations attributes that are inserted into bytecode when annotations are present. Future versions of BCEL should include this functionality without the need for a plugin.
This compile time bytecode instrumentation approach is shown in the directory code/02_compiletime of the sample code.
There are a number of disadvantages to this approach, however. For one thing, I had to add an additional step to my build process. I also cannot turn the instrumentation on or off based on command-line settings or other information that is not available at compile time. If both instrumented and non-instrumented code needs to be run in a production setting, two separate .jars will need to be built and the decision of which to use must be all or nothing.
Instrumenting Bytecode at Class Loading Time
A better solution would be to delay the instrumentation of our bytecode until after it is loaded from the disk. This way, the instrumented bytecode does not need to be stored. The start-up performance of our application may suffer, but the trade-off is that you can control what happens based on system properties, or other runtime configuration data.
Prior to Java 1.5, it was possible to do this type of class-file manipulation with a custom class loader. However, the new java.lang.instrument package added in Java 1.5 provides a few additional tools. In particular, it defines the concept of a ClassFileTransformer, which can be used to instrument a class during the standard loading process.
To register our ClassFileTransformer at the appropriate time (before any of our classes are loaded), I'll need to define a premain method. Java will call this before the main class is even loaded, and it is passed a reference to an Instrumentation object. I will also need to add a -javaagent option to the command line to tell Java about our premain method with Java. This argument takes the full name of your agent class (which contains the premain method) and an arbitrary string argument. In this case, we'll pass the full name of our Instrumentor class as the argument (this should all be on one line):
-javaagent:boxpeeking.instrument.InstrumentorAdaptor=
boxpeeking.status.instrument.StatusInstrumentor
Now that I've arranged for a callback that occurs before any annotated classes are loaded, and I have a reference to the Instrumentation object, I can register our ClassFileTransformer.
public static void premain (String className,
Instrumentation i)
throws ClassNotFoundException,
InstantiationException,
IllegalAccessException
{
Class instClass = Class.forName(className);
Instrumentor inst = (Instrumentor)instClass.newInstance();
i.addTransformer(new InstrumentorAdaptor(inst));
}
The adaptor that is registered will need to act as a bridge between the Instrumentor interface given above and Java's ClassFileTransformer interface.
public class InstrumentorAdaptor
implements ClassFileTransformer
{
public byte[] transform (ClassLoader cl,
String className,
Class classBeingRedefined,
ProtectionDomain protectionDomain,
byte[] classfileBuffer)
{
try {
ClassParser cp =
new ClassParser(
new ByteArrayInputStream(classfileBuffer),
className + ".java");
JavaClass jc = cp.parse();
ClassGen cg = new ClassGen(jc);
for (Annotation an :
getAnnotations(jc.getAttributes())) {
instrumentor.instrumentClass(cg, an);
}
for (org.apache.bcel.classfile.Method m :
cg.getMethods()) {
for (Annotation an :
getAnnotations(m.getAttributes())) {
ConstantPoolGen cpg =
cg.getConstantPool();
MethodGen mg =
new MethodGen(m, className, cpg);
instrumentor.instrumentMethod(cg, mg, an);
mg.setMaxStack();
mg.setMaxLocals();
cg.replaceMethod(m, mg.getMethod());
}
}
JavaClass jcNew = cg.getJavaClass();
return jcNew.getBytes();
} catch (Exception ex) {
throw new RuntimeException("instrumenting " +
className, ex);
}
}
...
}
This approach of instrumenting bytecode at startup is shown in the example code's /code/03_startup directory.
Conclusion
In this article, I have replaced a hard-coded solution with one that uses metaprogramming based on annotations and instrumentation. Although I've eliminated the need for any extra steps in our build process, my solution still has a number of limitations. In the next installment, I will explore a completely different implementation that uses thread sampling, and then combine these two techniques to create a solution that gives the best features of each. I will also discuss a number of additional requirements, including a progress bar and dynamic status messages.
References
Example source code for this article
"JSR-175 Public Draft Specification: A Program Annotation Facility for the JavaTM Programming Language"
BCEL web site: jakarta.apache.org/bcel
XDoclet web site: http://xdoclet.sourceforge.net
"J2SE 1.5 in a Nutshell"
"Declarative Programming in Java"
Java Annotations
Many APIs require a fair amount of boilerplate code. For example, in order to write a JAX-RPC web service, you must provide a paired interface and implementation. This boilerplate could be generated automatically by a tool if the program were “decorated” with annotations indicating which methods were remotely accessible.
Other APIs require “side files” to be maintained in parallel with programs. For example JavaBeans requires a BeanInfo class to be maintained in parallel with a bean, and Enterprise JavaBeans (EJB) requires a deployment descriptor. It would be more convenient and less error-prone if the information in these side files were maintained as annotations in the program itself.
The Java platform has always had various ad hoc annotation mechanisms. For example the transient modifier is an ad hoc annotation indicating that a field should be ignored by the serialization subsystem, and the @deprecated javadoc tag is an ad hoc annotation indicating that the method should no longer be used. As of release 5.0, the platform has a general purpose annotation (also known as metadata) facility that permits you to define and use your own annotation types. The facility consists of a syntax for declaring annotation types, a syntax for annotating declarations, APIs for reading annotations, a class file representation for annotations, and an annotation processing tool.
Annotations do not directly affect program semantics, but they do affect the way programs are treated by tools and libraries, which can in turn affect the semantics of the running program. Annotations can be read from source files, class files, or reflectively at run time.
Annotations complement javadoc tags. In general, if the markup is intended to affect or produce documentation, it should probably be a javadoc tag; otherwise, it should be an annotation.
Typical application programmers will never have to define an annotation type, but it is not hard to do so. Annotation type declarations are similar to normal interface declarations. An at-sign (@) precedes the interface keyword. Each method declaration defines an element of the annotation type. Method declarations must not have any parameters or a throws clause. Return types are restricted to primitives, String, Class, enums, annotations, and arrays of the preceding types. Methods can have default values. Here is an example annotation type declaration:
/**
* Describes the Request-For-Enhancement(RFE) that led
* to the presence of the annotated API element.
*/
public @interface RequestForEnhancement {
int id();
String synopsis();
String engineer() default "[unassigned]";
String date(); default "[unimplemented]";
}
Once an annotation type is defined, you can use it to annotate declarations. An annotation is a special kind of modifier, and can be used anywhere that other modifiers (such as public, static, or final) can be used. By convention, annotations precede other modifiers. Annotations consist of an at-sign (@) followed by an annotation type and a parenthesized list of element-value pairs. The values must be compile-time constants. Here is a method declaration with an annotation corresponding to the annotation type declared above:
@RequestForEnhancement(
id = 2868724,
synopsis = "Enable time-travel",
engineer = "Mr. Peabody",
date = "4/1/3007"
)
public static void travelThroughTime(Date destination) { ... }
An annotation type with no elements is termed a marker annotation type, for example:
/**
* Indicates that the specification of the annotated API element
* is preliminary and subject to change.
*/
public @interface Preliminary { }
It is permissible to omit the parentheses in marker annotations, as shown below:
@Preliminary public class TimeTravel { ... }
In annotations with a single element, the element should be named value, as shown below:
/**
* Associates a copyright notice with the annotated API element.
*/
public @interface Copyright {
String value();
}
It is permissible to omit the element name and equals sign (=) in a single-element annotation whose element name is value, as shown below:
@Copyright("2002 Yoyodyne Propulsion Systems")
public class OscillationOverthruster { ... }
To tie it all together, we'll build a simple annotation-based test framework. First we need a marker annotation type to indicate that a method is a test method, and should be run by the testing tool:
import java.lang.annotation.*;
/**
* Indicates that the annotated method is a test method.
* This annotation should be used only on parameterless static methods.
*/
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface Test { }
Note that the annotation type declaration is itself annotated. Such annotations are called meta-annotations. The first (@Retention(RetentionPolicy.RUNTIME)) indicates that annotations with this type are to be retained by the VM so they can be read reflectively at run-time. The second (@Target(ElementType.METHOD)) indicates that this annotation type can be used to annotate only method declarations.
Here is a sample program, some of whose methods are annotated with the above interface:
public class Foo {
@Test public static void m1() { }
public static void m2() { }
@Test public static void m3() {
throw new RuntimeException("Boom");
}
public static void m4() { }
@Test public static void m5() { }
public static void m6() { }
@Test public static void m7() {
throw new RuntimeException("Crash");
}
public static void m8() { }
}
Here is the testing tool:
import java.lang.reflect.*;
public class RunTests {
public static void main(String[] args) throws Exception {
int passed = 0, failed = 0;
for (Method m : Class.forName(args[0]).getMethods()) {
if (m.isAnnotationPresent(Test.class)) {
try {
m.invoke(null);
passed++;
} catch (Throwable ex) {
System.out.printf("Test %s failed: %s %n", m, ex.getCause());
failed++;
}
}
}
System.out.printf("Passed: %d, Failed %d%n", passed, failed);
}
}
The tool takes a class name as a command line argument and iterates over all the methods of the named class attempting to invoke each method that is annotated with the Test annotation type (defined above). The reflective query to find out if a method has a Test annotation is highlighted in green. If a test method invocation throws an exception, the test is deemed to have failed, and a failure report is printed. Finally, a summary is printed showing the number of tests that passed and failed. Here is how it looks when you run the testing tool on the Foo program (above):
$ java RunTests Foo
Test public static void Foo.m3() failed: java.lang.RuntimeException: Boom
Test public static void Foo.m7() failed: java.lang.RuntimeException: Crash
Passed: 2, Failed 2
While this testing tool is clearly a toy, it demonstrates the power of annotations and could easily be extended to overcome its limitations.
(http://java.sun.com/j2se/1.5.0/docs/guide/language/annotations.html)
Other APIs require “side files” to be maintained in parallel with programs. For example JavaBeans requires a BeanInfo class to be maintained in parallel with a bean, and Enterprise JavaBeans (EJB) requires a deployment descriptor. It would be more convenient and less error-prone if the information in these side files were maintained as annotations in the program itself.
The Java platform has always had various ad hoc annotation mechanisms. For example the transient modifier is an ad hoc annotation indicating that a field should be ignored by the serialization subsystem, and the @deprecated javadoc tag is an ad hoc annotation indicating that the method should no longer be used. As of release 5.0, the platform has a general purpose annotation (also known as metadata) facility that permits you to define and use your own annotation types. The facility consists of a syntax for declaring annotation types, a syntax for annotating declarations, APIs for reading annotations, a class file representation for annotations, and an annotation processing tool.
Annotations do not directly affect program semantics, but they do affect the way programs are treated by tools and libraries, which can in turn affect the semantics of the running program. Annotations can be read from source files, class files, or reflectively at run time.
Annotations complement javadoc tags. In general, if the markup is intended to affect or produce documentation, it should probably be a javadoc tag; otherwise, it should be an annotation.
Typical application programmers will never have to define an annotation type, but it is not hard to do so. Annotation type declarations are similar to normal interface declarations. An at-sign (@) precedes the interface keyword. Each method declaration defines an element of the annotation type. Method declarations must not have any parameters or a throws clause. Return types are restricted to primitives, String, Class, enums, annotations, and arrays of the preceding types. Methods can have default values. Here is an example annotation type declaration:
/**
* Describes the Request-For-Enhancement(RFE) that led
* to the presence of the annotated API element.
*/
public @interface RequestForEnhancement {
int id();
String synopsis();
String engineer() default "[unassigned]";
String date(); default "[unimplemented]";
}
Once an annotation type is defined, you can use it to annotate declarations. An annotation is a special kind of modifier, and can be used anywhere that other modifiers (such as public, static, or final) can be used. By convention, annotations precede other modifiers. Annotations consist of an at-sign (@) followed by an annotation type and a parenthesized list of element-value pairs. The values must be compile-time constants. Here is a method declaration with an annotation corresponding to the annotation type declared above:
@RequestForEnhancement(
id = 2868724,
synopsis = "Enable time-travel",
engineer = "Mr. Peabody",
date = "4/1/3007"
)
public static void travelThroughTime(Date destination) { ... }
An annotation type with no elements is termed a marker annotation type, for example:
/**
* Indicates that the specification of the annotated API element
* is preliminary and subject to change.
*/
public @interface Preliminary { }
It is permissible to omit the parentheses in marker annotations, as shown below:
@Preliminary public class TimeTravel { ... }
In annotations with a single element, the element should be named value, as shown below:
/**
* Associates a copyright notice with the annotated API element.
*/
public @interface Copyright {
String value();
}
It is permissible to omit the element name and equals sign (=) in a single-element annotation whose element name is value, as shown below:
@Copyright("2002 Yoyodyne Propulsion Systems")
public class OscillationOverthruster { ... }
To tie it all together, we'll build a simple annotation-based test framework. First we need a marker annotation type to indicate that a method is a test method, and should be run by the testing tool:
import java.lang.annotation.*;
/**
* Indicates that the annotated method is a test method.
* This annotation should be used only on parameterless static methods.
*/
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface Test { }
Note that the annotation type declaration is itself annotated. Such annotations are called meta-annotations. The first (@Retention(RetentionPolicy.RUNTIME)) indicates that annotations with this type are to be retained by the VM so they can be read reflectively at run-time. The second (@Target(ElementType.METHOD)) indicates that this annotation type can be used to annotate only method declarations.
Here is a sample program, some of whose methods are annotated with the above interface:
public class Foo {
@Test public static void m1() { }
public static void m2() { }
@Test public static void m3() {
throw new RuntimeException("Boom");
}
public static void m4() { }
@Test public static void m5() { }
public static void m6() { }
@Test public static void m7() {
throw new RuntimeException("Crash");
}
public static void m8() { }
}
Here is the testing tool:
import java.lang.reflect.*;
public class RunTests {
public static void main(String[] args) throws Exception {
int passed = 0, failed = 0;
for (Method m : Class.forName(args[0]).getMethods()) {
if (m.isAnnotationPresent(Test.class)) {
try {
m.invoke(null);
passed++;
} catch (Throwable ex) {
System.out.printf("Test %s failed: %s %n", m, ex.getCause());
failed++;
}
}
}
System.out.printf("Passed: %d, Failed %d%n", passed, failed);
}
}
The tool takes a class name as a command line argument and iterates over all the methods of the named class attempting to invoke each method that is annotated with the Test annotation type (defined above). The reflective query to find out if a method has a Test annotation is highlighted in green. If a test method invocation throws an exception, the test is deemed to have failed, and a failure report is printed. Finally, a summary is printed showing the number of tests that passed and failed. Here is how it looks when you run the testing tool on the Foo program (above):
$ java RunTests Foo
Test public static void Foo.m3() failed: java.lang.RuntimeException: Boom
Test public static void Foo.m7() failed: java.lang.RuntimeException: Crash
Passed: 2, Failed 2
While this testing tool is clearly a toy, it demonstrates the power of annotations and could easily be extended to overcome its limitations.
(http://java.sun.com/j2se/1.5.0/docs/guide/language/annotations.html)
Tuesday, May 02, 2006
Understanding W3C Schema Complex Types By Donald Smith
Are W3C XML Schema complex types so difficult to understand that you shouldn't even bother trying? Kohsuke Kawaguchi thinks so; or so he claimed in his recent XML.com article, in which he offered assurances that you can write complex types without understanding them.
My response to that assertion is to ask why would you want to write complex types without understanding them, especially when they are easily understandable? There are four things you need to know in order to understand complex types in W3C Schemas. These four things are easy to understand. See what you think about Kawaguchi's argument after learning them.
One of the most important, but least emphasized, aspects of W3C schemas is the type hierarchy. The importance of the type hierarchy can hardly be overstated. Why? Because the syntax for expressing types in schemas follows precisely from the type hierarchy.
XML Schema Part 2: Datatypes, Section 3 contains a helpful graphic that explains the schema type hierarchy.
Schema types form a hierarchy because they all derive, directly or indirectly, from the root type. The root type is anyType. (You can actually use anyType in an element declaration; it allows any content whatsoever.) The type hierarchy first branches into two groups: simple types and complex types. Here we encounter the first two of the four things you need to know in order to understand complex types: first, derivation is the basis of connection between types in the type hierarchy; and, second, the initial branching of the hierarchy is into simple and complex types.
Kinds of Derivation
To derive a type means to take an existing type (called the "base") and modify it in some way so as to produce a new type. There are four kinds of derivation: restriction, extension, list, and union. This discussion looks at derivation by restriction and extension since they are the most commonly used.
Derivation by restriction takes an existing type as the base and creates a new type by limiting its allowed content to a subset of that allowed by the base type. Derivation by extension takes an existing type as the base and creates a new type by adding to its allowed content.
Simple Types versus Complex Types
Simple types and complex types differ in this way: simple types cannot have element children or attributes; complex types may have element children and attributes.
Tracing the type hierarchy down the branch of simple types, we see that the first simple type is anySimpleType, which is also type that you could actually use. W3C XML Schemas has 44 built-in simple types, each of which derives from anySimpleType, and all but three of which derive by restriction. Not a single one derives by extension. To extend a simple type would mean to add element children or an attribute. This contradicts the definition of simple type in W3C Schemas and is thus prohibited.
Deriving a New Simple Type
Thinking in terms of the type hierarchy, it ought to be relatively straightforward to derive a new simple, "myNameType", that restricts its base type, "string", to a specific, fixed subset, "Don Smith". The W3C XML Schema fragment for expressing myNameType is
As you can see, the XML Schema for this type definition follows the type hierarchy exactly -- except for the enumeration element, one of the twelve facets that can be used to qualify types. We won't look at facets here since our concern is with the relation between the type hierarchy and the Schema syntax for expressing types. I simply used this one to complete the example. (See Table B1.a.Simple Types and Applicable Facets in Schema Part 0: Primer for a convenient list of facets.)
Now I just have to associate myNameType with an element, and then I can use the type in an XML document. So the element declaration
lets me useDon Smith in an XML document instance.
What about Complex Types?
Does the syntax for complex types also follow the logic of the type hierarchy? Yes. But the type hierarchy diagram doesn't help us at this point because it doesn't provide two crucial pieces of information about complex types. However, once you understand these two points, complex types lose their indecipherable complexity and become quite intelligible.
The Two Forms of Complex Types
Complex types are divided into two groups: those with simple content and those with complex content. And that leads us to the third thing you need to know in order to understand complex types: while both forms of complex type allow attributes, only those with complex content allow child elements; those with simple content only allow character content.
In other words, the difference between complex types with simple content and complex types with complex content is that the former do not allow element children while the latter do. That's it. The two forms of complex type are represented in what I call the Schema Type Decision Tree (PDF) under the complex type branch.
Let's suppose I want to add an attribute to myNameType. Adding an attribute to a simple type always moves it into the complex type branch of the type hierarchy. Once on the complex type branch, I must ask a second question. Do I want the new type to allow element content? If I don't, then my new type must be a complex type with simple content. After that, I simply take dc:myNameType as the base type and extend it by adding an attribute:
Now, after declaring my element "employee" to be of myNewNameType, I can have Don Smith in my XML document instance.
It may seem odd that adding an attribute to a simple type requires the creation of a new complex type, one that has simple content to boot. But that's the logic of the type hierarchy: a type that has attributes must be a complex type, and that type can either allow element children or not. Perhaps an odd logic, but it is intelligible.
Let's suppose now that I want my complex type to have child elements. That requires a complex type with complex content. So I simply add my content model and attributes (if any). That's easy. But maybe too easy. We must be careful not skip over a crucial fact that makes a big difference.
Adding a content model is still a derivation of a new type from some base type. If I do not take an existing complex type as the base for the new derivation, what will I use for a base type? I'll use anyType. The vast majority of types that allow element content are restrictions of anyType. For example,
The type associated with "employee" now has an element named "name" followed by an element named "location". Further, personnel can have an attribute named "position":
Don Smith
Dallas, TX
The logic behind the syntax is straightforward. I want a type that allows child elements. That requires a complex type with complex content, while still deriving a new type from a base type. In this case I'm restricting anyType; I could as easily extend another type. I add my content model and an attribute declaration. I'm done, and it was all pretty easy.
Ambushed by Abbreviation
But can't this be expressed more concisely? Yes, it can. There is an abbreviated form for all complex type definitions that have complex content and restrict anyType. You simply leave out the and elements:
This type definition is equivalent to the previous one. And that leads us to the fourth thing you need to know in order to understand complex types: the default syntax for complex types is complex content that restricts anyType.
Why didn't I show you the abbreviated syntax first? Because the abbreviation obscures the logic behind the default syntax. If all you see is followed by a content model, it's totally confusing as to why complex types sometimes have or child elements or, often, neither.
Now that you know the logic behind the two forms of complex type, you won't be confused when you see a complex type that has neither nor . You know what the default is.
Those Tricky Empty Elements
Writing type definitions for empty elements turns out to be counter-intuitive, but, fortunately, the logic behind the complex type syntax still holds. Remember that an empty element is one that has neither data content nor child elements. It may have an attribute. Let's take the case of an empty element that doesn't have an attribute.
Your first inclination might be to associate the empty element with a simple type. But that won't work since simple types allow data content. So it must be a complex type. The, ask yourself the next question. Will it allow element children? No. We need a with , right?
Wrong. Complex types with simple content also allow data content, and we want an empty element. That leaves us with with , which ensures that there will not be any data content in the element. But we don't want child elements, either, and a complex type with complex content allows child elements. The key is that it doesn't require them. What do we do? Simply leave the content model out of the type definition:
Our type definition, now associated with the element "callMyApp", allows the markup to occur in my XML document instance.
Now apply the default syntax for complex types to this type definition. An definition equivalent to the one above is
It's no wonder that people get confused about complex types. They generally don't realize that all complex types are divisible into two kinds: those with simple content and those with complex content. The reason why people don't generally realize this is because they normally learn the abbreviated syntax first. But, as we've seen, if you learn the full syntax and the logic behind it first, then the abbreviated syntax, and complex types in general, cease to be a befuddingly conundrum.
If all of this is now as clear to you as it is to me, you don't have to trust anyone's assurances that you should use complex types without understanding them. You can now use and understand them.
(http://www.xml.com/lpt/a/2001/08/22/easyschema.html)
My response to that assertion is to ask why would you want to write complex types without understanding them, especially when they are easily understandable? There are four things you need to know in order to understand complex types in W3C Schemas. These four things are easy to understand. See what you think about Kawaguchi's argument after learning them.
One of the most important, but least emphasized, aspects of W3C schemas is the type hierarchy. The importance of the type hierarchy can hardly be overstated. Why? Because the syntax for expressing types in schemas follows precisely from the type hierarchy.
XML Schema Part 2: Datatypes, Section 3 contains a helpful graphic that explains the schema type hierarchy.
Schema types form a hierarchy because they all derive, directly or indirectly, from the root type. The root type is anyType. (You can actually use anyType in an element declaration; it allows any content whatsoever.) The type hierarchy first branches into two groups: simple types and complex types. Here we encounter the first two of the four things you need to know in order to understand complex types: first, derivation is the basis of connection between types in the type hierarchy; and, second, the initial branching of the hierarchy is into simple and complex types.
Kinds of Derivation
To derive a type means to take an existing type (called the "base") and modify it in some way so as to produce a new type. There are four kinds of derivation: restriction, extension, list, and union. This discussion looks at derivation by restriction and extension since they are the most commonly used.
Derivation by restriction takes an existing type as the base and creates a new type by limiting its allowed content to a subset of that allowed by the base type. Derivation by extension takes an existing type as the base and creates a new type by adding to its allowed content.
Simple Types versus Complex Types
Simple types and complex types differ in this way: simple types cannot have element children or attributes; complex types may have element children and attributes.
Tracing the type hierarchy down the branch of simple types, we see that the first simple type is anySimpleType, which is also type that you could actually use. W3C XML Schemas has 44 built-in simple types, each of which derives from anySimpleType, and all but three of which derive by restriction. Not a single one derives by extension. To extend a simple type would mean to add element children or an attribute. This contradicts the definition of simple type in W3C Schemas and is thus prohibited.
Deriving a New Simple Type
Thinking in terms of the type hierarchy, it ought to be relatively straightforward to derive a new simple, "myNameType", that restricts its base type, "string", to a specific, fixed subset, "Don Smith". The W3C XML Schema fragment for expressing myNameType is
As you can see, the XML Schema for this type definition follows the type hierarchy exactly -- except for the enumeration element, one of the twelve facets that can be used to qualify types. We won't look at facets here since our concern is with the relation between the type hierarchy and the Schema syntax for expressing types. I simply used this one to complete the example. (See Table B1.a.Simple Types and Applicable Facets in Schema Part 0: Primer for a convenient list of facets.)
Now I just have to associate myNameType with an element, and then I can use the type in an XML document. So the element declaration
lets me use
What about Complex Types?
Does the syntax for complex types also follow the logic of the type hierarchy? Yes. But the type hierarchy diagram doesn't help us at this point because it doesn't provide two crucial pieces of information about complex types. However, once you understand these two points, complex types lose their indecipherable complexity and become quite intelligible.
The Two Forms of Complex Types
Complex types are divided into two groups: those with simple content and those with complex content. And that leads us to the third thing you need to know in order to understand complex types: while both forms of complex type allow attributes, only those with complex content allow child elements; those with simple content only allow character content.
In other words, the difference between complex types with simple content and complex types with complex content is that the former do not allow element children while the latter do. That's it. The two forms of complex type are represented in what I call the Schema Type Decision Tree (PDF) under the complex type branch.
Let's suppose I want to add an attribute to myNameType. Adding an attribute to a simple type always moves it into the complex type branch of the type hierarchy. Once on the complex type branch, I must ask a second question. Do I want the new type to allow element content? If I don't, then my new type must be a complex type with simple content. After that, I simply take dc:myNameType as the base type and extend it by adding an attribute:
Now, after declaring my element "employee" to be of myNewNameType, I can have
It may seem odd that adding an attribute to a simple type requires the creation of a new complex type, one that has simple content to boot. But that's the logic of the type hierarchy: a type that has attributes must be a complex type, and that type can either allow element children or not. Perhaps an odd logic, but it is intelligible.
Let's suppose now that I want my complex type to have child elements. That requires a complex type with complex content. So I simply add my content model and attributes (if any). That's easy. But maybe too easy. We must be careful not skip over a crucial fact that makes a big difference.
Adding a content model is still a derivation of a new type from some base type. If I do not take an existing complex type as the base for the new derivation, what will I use for a base type? I'll use anyType. The vast majority of types that allow element content are restrictions of anyType. For example,
The type associated with "employee" now has an element named "name" followed by an element named "location". Further, personnel can have an attribute named "position":
The logic behind the syntax is straightforward. I want a type that allows child elements. That requires a complex type with complex content, while still deriving a new type from a base type. In this case I'm restricting anyType; I could as easily extend another type. I add my content model and an attribute declaration. I'm done, and it was all pretty easy.
Ambushed by Abbreviation
But can't this be expressed more concisely? Yes, it can. There is an abbreviated form for all complex type definitions that have complex content and restrict anyType. You simply leave out the
This type definition is equivalent to the previous one. And that leads us to the fourth thing you need to know in order to understand complex types: the default syntax for complex types is complex content that restricts anyType.
Why didn't I show you the abbreviated syntax first? Because the abbreviation obscures the logic behind the default syntax. If all you see is
Now that you know the logic behind the two forms of complex type, you won't be confused when you see a complex type that has neither
Those Tricky Empty Elements
Writing type definitions for empty elements turns out to be counter-intuitive, but, fortunately, the logic behind the complex type syntax still holds. Remember that an empty element is one that has neither data content nor child elements. It may have an attribute. Let's take the case of an empty element that doesn't have an attribute.
Your first inclination might be to associate the empty element with a simple type. But that won't work since simple types allow data content. So it must be a complex type. The, ask yourself the next question. Will it allow element children? No. We need a
Wrong. Complex types with simple content also allow data content, and we want an empty element. That leaves us with
Our type definition, now associated with the element "callMyApp", allows the markup
Now apply the default syntax for complex types to this type definition. An definition equivalent to the one above is
It's no wonder that people get confused about complex types. They generally don't realize that all complex types are divisible into two kinds: those with simple content and those with complex content. The reason why people don't generally realize this is because they normally learn the abbreviated syntax first. But, as we've seen, if you learn the full syntax and the logic behind it first, then the abbreviated syntax, and complex types in general, cease to be a befuddingly conundrum.
If all of this is now as clear to you as it is to me, you don't have to trust anyone's assurances that you should use complex types without understanding them. You can now use and understand them.
(http://www.xml.com/lpt/a/2001/08/22/easyschema.html)
The Java IAQ: Infrequently Answered Questions by Peter Norvig
--------------------------------------------------------------------------------
Q: What is an Infrequently Answered Question?
A question is infrequently answered either because few people know the answer or because it is about an obscure, subtle point (but a point that may be crucial to you). I thought I had invented the term, but it also shows up at the very informative About.com Urban Legends site. There are lots of Java FAQs around, but this is the only Java IAQ. (There are a few Infrequently Asked Questions lists, including a satirical one on C.)
--------------------------------------------------------------------------------
Q:The code in a finally clause will never fail to execute, right?
Well, hardly ever. But here's an example where the finally code will not execute, regardless of the value of the boolean choice:
try {
if (choice) {
while (true) ;
} else {
System.exit(1);
}
} finally {
code.to.cleanup();
}
--------------------------------------------------------------------------------
Q:Within a method m in a class C, isn't this.getClass() always C?
No. It's possible that for some object x that is an instance of some subclass C1 of C either there is no C1.m() method, or some method on x called super.m(). In either case, this.getClass() is C1, not C within the body of C.m(). If C is final, then you're ok.
--------------------------------------------------------------------------------
Q: I defined an equals method, but Hashtable ignores it. Why?
equals methods are surprisngly hard to get right. Here are the places to look first for a problem:
You defined the wrong equals method. For example, you wrote:
public class C {
public boolean equals(C that) { return id(this) == id(that); }
}
But in order for table.get(c) to work you need to make the equals method take an Object as the argument, not a C:
public class C {
public boolean equals(Object that) {
return (that instanceof C) && id(this) == id((C)that);
}
}
Why? The code for Hashtable.get looks something like this:
public class Hashtable {
public Object get(Object key) {
Object entry;
...
if (entry.equals(key)) ...
}
}
Now the method invoked by entry.equals(key) depends upon the actual run-time type of the object referenced by entry, and the declared, compile-time type of the variable key. So when you as a user call table.get(new C(...)), this looks in class C for the equals method with argument of type Object. If you happen to have defined an equals method with argument of type C, that's irrelevent. It ignores that method, and looks for a method with signature equals(Object), eventually finding Object.equals(Object). If you want to over-ride a method, you need to match argument types exactly. In some cases, you may want to have two methods, so that you don't pay the overhead of casting when you know you have an object of the right class:
public class C {
public boolean equals(Object that) {
return (this == that)
|| ((that instanceof C) && this.equals((C)that));
}
public boolean equals(C that) {
return id(this) == id(that); // Or whatever is appropriate for class C
}
}
You didn't properly implement equals as an equality predicate: equals must be symmetric, transitive, and reflexive. Symmetric means a.equals(b) must have the same value as b.equals(a). (This is the one most people mess up.) Transitive means that if a.equals(b) and b.equals(c) then a.equals(c) must be true. Reflexive means that a.equals(a) must be true, and is the reason for the (this == that) test above (it's also often good practice to include this because of efficiency reasons: testing for == is faster than looking at all the slots of an object, and to partially break the recursion problem on objects that might have circular pointer chains).
You forgot the hashCode method. Anytime you define a equals method, you should also define a hashCode method. You must make sure that two equal objects have the same hashCode, and if you want better hashtable performance, you should try to make most non-equal objects have different hashCodes. Some classes cache the hash code in a private slot of an object, so that it need be computed only once. If that is the case then you will probably save time in equals if you include a line that says if (this.hashSlot != that.hashSlot) return false.
You didn't handle inheritance properly. First of all, consider if two objects of different class can be equal. Before you say "NO! Of course not!" consider a class Rectangle with width and height fields, and a Box class, which has the above two fields plus depth. Is a Box with depth == 0 equal to the equivalent Rectangle? You might want to say yes. If you are dealing with a non-final class, then it is possible that your class might be subclassed, and you will want to be a good citizen with respect to your subclass. In particular, you will want to allow an extender of your class C to use your C.equals method using super as follows:
public class C2 extends C {
int newField = 0;
public boolean equals(Object that) {
if (this == that) return true;
else if (!(that instanceof C2)) return false;
else return this.newField == ((C2)that).newField) && super.equals(that);
}
}
To allow this to work, you have to be careful about how you treat classes in your definition of C.equals. For example, check for that instanceof C rather than that.getClass() == C.class. See the previous IAQ question to learn why. Use this.getClass() == that.getClass() if you are sure that two objects must be of the same class to be considered equals.
You didn't handle circular references properly. Consider:
public class LinkedList {
Object contents;
LinkedList next = null;
public boolean equals(Object that) {
return (this == that)
|| ((that instanceof LinkedList) && this.equals((LinkedList)that));
}
public boolean equals(LinkedList that) { // Buggy!
return Util.equals(this.contents, that.contents) &&
Util.equals(this.next, that.next);
}
}
Here I have assumed there is a Util class with:
public static boolean equals(Object x, Object y) {
return (x == y) || (x != null && x.equals(y));
}
I wish this method were in Object; without it you always have to throw in tests against null. Anyway, the LinkedList.equals method will never return if asked to compare two LinkedLists with circular references in them (a pointer from one element of the linked list back to another element). See the description of the Common Lisp function list-length for an explanation of how to handle this problem in linear time with only two words of extra storge. (I don't give the answer here in case you want to try to figure it out for yourself first.)
--------------------------------------------------------------------------------
Q: I tried to forward a method to super, but it occasionally doesn't work. Why?
This is the code in question, simplified for this example:
/** A version of Hashtable that lets you do
* table.put("dog", "canine");, and then have
* table.get("dogs") return "canine". **/
public class HashtableWithPlurals extends Hashtable {
/** Make the table map both key and key + "s" to value. **/
public Object put(Object key, Object value) {
super.put(key + "s", value);
return super.put(key, value);
}
}
You need to be careful when passing to super that you fully understand what the super method does. In this case, the contract for Hashtable.put is that it will record a mapping between the key and the value in the table. However, if the hashtable gets too full, then Hashtable.put will allocate a larger array for the table, copy all the old objects over, and then recursively re-call table.put(key, value). Now, because Java resolves methods based on the runtime type of the target, in our example this recursive call within the code for Hashtable will go to HashtableWithPlurals.put(key, value), and the net result is that occasionally (when the size of the table overflows at just the wrong time), you will get an entry for "dogss" as well as for "dogs" and "dog". Now, does it state anywhere in the documentation for put that doing this recursive call is a possibility? No. In cases like this, it sure helps to have source code access to the JDK.
--------------------------------------------------------------------------------
Q: Why does my Properties object ignore the defaults when I do a get?
You shouldn't do a get on a Properties object; you should do a getProperty instead. Many people assume that the only difference is that getProperty has a declared return type of String, while get is declared to return an Object. But actually there is a bigger difference: getProperty looks at the defaults. get is inherited from Hashtable, and it ignores the default, thereby doing exactly what is documented in the Hashtable class, but probably not what you expect. Other methods that are inherited from Hashtable (like isEmpty and toString) will also ignore defaults. Example code:
Properties defaults = new Properties();
defaults.put("color", "black");
Properties props = new Properties(defaults);
System.out.println(props.get("color") + ", " +
props.getProperty(color));
// This prints "null, black"
Is this justified by the documentation? Maybe. The documentation in Hashtable talks about entries in the table, and the behavior of Properties is consistent if you assume that defauls are not entries in the table. If for some reason you thought defaults were entries (as you might be led to believe by the behavior of getProperty) then you will be confused.
--------------------------------------------------------------------------------
Q:Inheritance seems error-prone. How can I guard against these errors?
The previous two questions show that a programmer neeeds to be very careful when extending a class, and sometimes just in using a class that extends another class. Problems like these two lead John Ousterhout to say "Implementation inheritance causes the same intertwining and brittleness that have been observed when goto statements are overused. As a result, OO systems often suffer from complexity and lack of reuse." (Scripting, IEEE Computer, March 1998) and Edsger Dijkstra to allegedly say "Object-oriented programming is an exceptionally bad idea which could only have originated in California." (from a collection of signature files). I don't think there's a general way to insure being safe, but there are a few things to be aware of:
Extending a class that you don't have source code for is always risky; the documentation may be incomplete in ways you can't foresee.
Calling super tends to make these unforeseen problems jump out.
You need to pay as much attention to the methods that you don't over-ride as the methods that you do. This is one of the big fallacies of Object-Oriented design using inheritance. It is true that inheritance lets you write less code. But you still have to think about the code you don't write.
You're especially looking for trouble if the subclass changes the contract of any of the methods, or of the class as a whole. It is difficult to tell when a contract is changed, since contracts are informal (there is a formal part in the type signature, but the rest appears only in comments). In the Properties example, it is not clear if a contract is being broken, because it is not clear if the defaults are to be considered "entries" in the table or not.
--------------------------------------------------------------------------------
Q:What are some alternatives to inheritance?
Delegation is an alternative to inheritance. Delegation means that you include an instance of another class as an instance variable, and forward messages to the instance. It is often safer than inheritance because it forces you to think about each message you forward, because the instance is of a known class, rather than a new class, and because it doesn't force you to accept all the methods of the super class: you can provide only the methods that really make sense. On the other hand, it makes you write more code, and it is harder to re-use (because it is not a subclass).
For the HashtableWithPlurals example, delegation would give you this (note: as of JDK 1.2, Dictionary is considered obsolete; use Map instead):
/** A version of Hashtable that lets you do
* table.put("dog", "canine");, and then have
* table.get("dogs") return "canine". **/
public class HashtableWithPlurals extends Dictionary {
Hashtable table = new Hashtable();
/** Make the table map both key and key + "s" to value. **/
public Object put(Object key, Object value) {
table.put(key + "s", value);
return table.put(key, value);
}
... // Need to implement other methods as well
}
The Properties example, if you wanted to enforce the interpretation that default values are entries, would be better done with delegation. Why was it done with inheritance, then? Because the Java implementation team was rushed, and took the course that required writing less code.
--------------------------------------------------------------------------------
Q: Why are there no global variables in Java?
Global variables are considered bad form for a variety of reasons:
Adding state variables breaks referential transparency (you no longer can understand a statement or expression on its own: you need to understand it in the context of the settings of the global variables).
State variables lessen the cohesion of a program: you need to know more to understand how something works. A major point of Object-Oriented programming is to break up global state into more easily understood collections of local state.
When you add one variable, you limit the use of your program to one instance. What you thought was global, someone else might think of as local: they may want to run two copies of your program at once.
For these reasons, Java decided to ban global variables.
--------------------------------------------------------------------------------
Q: I still miss global variables. What can I do instead?
That depends on what you want to do. In each case, you need to decide two things: how many copies of this so-called global variable do I need? And where would be a convenient place to put it? Here are some common solutions:
If you really want only one copy per each time a user invokes Java by starting up a Java virtual machine, then you probably want a static instance variable. For example, you have a MainWindow class in your application, and you want to count the number of windows that the user has opened, and initiate the "Really quit?" dialog when the user has closed the last one. For that, you want: // One variable per class (per JVM)
public Class MainWindow {
static int numWindows = 0;
...
// when opening: MainWindow.numWindows++;
// when closing: MainWindow.numWindows--;
}
In many cases, you really want a class instance variable. For example, suppose you wrote a web browser and wanted to have the history list as a global variable. In Java, it would make more sense to have the history list be an instance variable in the Browser class. Then a user could run two copies of the browser at once, in the same JVM, without having them step on each other. // One variable per instance
public class Browser {
HistoryList history = new HistoryList();
...
// Make entries in this.history
}
Now suppose that you have completed the design and most of the implementation of your browser, and you discover that, deep down in the details of, say, the Cookie class, inside the Http class, you want to display an error message. But you don't know where to display the message. You could easily add an instance variable to the Browser class to hold the display stream or frame, but you haven't passed the current instance of the browser down into the methods in the Cookie class. You don't want to change the signatures of many methods to pass the browser along. You can't use a static variable, because there might be multiple browsers running. However, if you can guarantee that there will be only one browser running per thread (even if each browser may have multiple threads) then there is a good solution: store a table of thread-to-browser mappings as a static variable in the Browser class, and look up the right browser (and hence display) to use via the current thread: // One "variable" per thread
public class Browser {
static Hashtable browsers = new Hashtable();
public Browser() { // Constructor
browsers.put(Thread.currentThread(), this);
}
...
public void reportError(String message) {
Thread t = Thread.currentThread();
((Browser)Browser.browsers.get(t))
.show(message)
}
}
Finally, suppose you want the value of a global variable to persist between invocations of the JVM, or to be shared among multiple JVMs in a network of machines. Then you probably should use a database which you access through JDBC, or you should serialize data and write it to a file.
--------------------------------------------------------------------------------
Q: Can I write sin(x) instead of Math.sin(x)?
Short answer: no. Get used to writing the class name to access static methods from outside the class. However, if you insist on a longer answer ...
If you only want a few methods, you can put in calls to them within your own class: public static double sin(double x) { return Math.sin(x); }
public static double cos(double x) { return Math.cos(x); }
...
sin(x)
Static methods take a target (thing to the left of the dot) that is either a class name, or is an object whose value is ignored, but must be declared to be of the right class. So you could save three characters per call by doing: // Can't instantiate Math, so it must be null.
Math m = null;
...
m.sin(x)
java.lang.Math is a final class, so you can't inherit from it, but if you have your own set of static methods that you would like to share among many of your own classes, then you can package them up and inherit them: public abstract class MyStaticMethods {
public static double mysin(double x) { ... }
}
public class MyClass1 extends MyStaticMethods {
...
mysin(x)
}
Peter van der Linden, author of Just Java, recommends against both of the last two practices in his FAQ. I agree with him that Math m = null is a bad idea in most cases, but I'm not convinced that the MyStaticMethods demonstrates "very poor OOP style to use inheritance to obtain a trivial name abbreviation (rather than to express a type hierarchy)." First of all, trivial is in the eye of the beholder; the abbreviation may be substantial. (See an example of how I used this approach to what I thought was good effect.) Second, it is rather presumptuous to say that this is very bad OOP style. You could make a case that it is bad Java style, but in languages with multiple inheritance, this idiom would be more acceptable.
Another way of looking at it is that features of Java (and any language) necessarily involve trade-offs, and conflate many issues. I agree it is bad to use inheritance in such a way that you mislead the user into thinking that MyClass1 is inheriting behavior from MyStaticMethods, and it is bad to prohibit MyClass1 from extending whatever other class it really wants to extend. But in Java the class is also the unit of encapsulation, compilation (mostly), and name scope. The MyStaticMethod approach scores negative points on the type hierarchy front, but positive points on the name scope front. If you say that the type hierarchy view is more important, I won't argue with you. But I will argue if you think of a class as doing only one thing, rather than many things at once, and if you think of style guides as absolute rather than as trade-offs.
--------------------------------------------------------------------------------
Q: Is null an Object?
Absolutely not. By that, I mean (null instanceof Object) is false. Some other things you should know about null:
You can't call a method on null: x.m() is an error when x is null and m is a non-static method. (When m is a static method it is fine, because it is the class of x that matters; the value is ignored.)
There is only one null, not one for each class. Thus, ((String) null == (Hashtable) null), for example.
It is ok to pass null as an argument to a method, as long as the method is expecting it. Some methods do; some do not. So, for example, System.out.println(null) is ok, but string.compareTo(null) is not. For methods you write, your javadoc comments should say whether null is ok, unless it is obvious.
In JDK 1.1 to 1.1.5, passing null as the literal argument to a constructor of an anonymous inner class (e.g., new SomeClass(null) { ...} caused a compiler error. It's ok to pass an expression whose value is null, or to pass a coerced null, like new SomeClass((String) null) { ...}
There are at least three different meanings that null is commonly used to express:
Uninitialized. A variable or slot that hasn't yet been assigned its real value.
Non-existant/not applicable. For example, terminal nodes in a binary tree might be represented by a regular node with null child pointers.
Empty. For example, you might use null to represent the empty tree. Note that this is subtly different from the previous case, although some people make the mistake of confusing the two cases. The difference is whether null is an acceptable tree node, or whether it is a signal to not treat the value as a tree node. Compare the following three implementations of binary tree nodes with an in-order print method:
// null means not applicable
// There is no empty tree.
class Node {
Object data;
Node left, right;
void print() {
if (left != null)
left.print();
System.out.println(data);
if (right != null)
right.print();
}
}
// null means empty tree
// Note static, non-static methods
class Node {
Object data;
Node left, right;
void static print(Node node) {
if (node != null) node.print();
}
void print() {
print(left);
System.out.println(data);
print(right);
}
}
// Separate class for Empty
// null is never used
interface Node { void print(); }
class DataNode implements Node{
Object data;
Node left, right;
void print() {
left.print();
System.out.println(data);
right.print();
}
}
class EmptyNode implements Node {
void print() { }
}
--------------------------------------------------------------------------------
Q: How big is an Object? Why is there no sizeof?
C has a sizeof operator, and it needs to have one, because the user has to manage calls to malloc, and because the size of primitive types (like long) is not standardized. Java doesn't need a sizeof, but it would still have been a convenient aid. Since it's not there, you can do this:
static Runtime runtime = Runtime.getRuntime();
...
long start, end;
Object obj;
runtime.gc();
start = runtime.freememory();
obj = new Object(); // Or whatever you want to look at
end = runtime.freememory();
System.out.println("That took " + (start-end) + "
bytes.");
This method is not foolproof, because a garbage collection could occur in the middle of the code you are instrumenting, throwing off the byte count. Also, if you are using a just-in-time compiler, some bytes may come from generating code.
You might be surprised to find that an Object takes 16 bytes, or 4 words, in the Sun JDK VM. This breaks down as follows: There is a two-word header, where one word is a pointer to the object's class, and the other points to the instance variables. Even though Object has no instance variables, Java still allocates one word for the variables. Finally, there is a "handle", which is another pointer to the two-word header. Sun says that this extra level of indirection makes garbage collection simpler. (There have been high performance Lisp and Smalltalk garbage collectors that do not use the extra level for at least 15 years. I have heard but have not confirmed that the Microsoft JVM does not have the extra level of indirection.)
An empty new String() takes 40 bytes, or 10 words: 3 words of pointer overhead, 3 words for the instance variables (the start index, end index, and character array), and 4 words for the empty char array. Creating a substring of an existing string takes "only" 6 words, because the char array is shared. Putting an Integer key and Integer value into a Hashtable takes 64 bytes (in addition to the four bytes that were pre-allocated in the Hashtable array): I'll let you work out why.
--------------------------------------------------------------------------------
Q: In what order is initialization code executed? What should I put where?
Instance variable initialization code can go in three places within a class:
In an instance variable initializer for a class (or a superclass). class C {
String var = "val";
In a constructor for a class (or a superclass). public C() { var = "val"; }
In an object initializer block. This is new in Java 1.1; its just like a static initializer block but without the keyword static. { var = "val"; }
}
The order of evaluation (ignoring out of memory problems) when you say new C() is:
Call a constructor for C's superclass (unless C is Object, in which case it has no superclass). It will always be the no-argument constructor, unless the programmer explicitly coded super(...) as the very first statement of the constructor.
Once the super constructor has returned, execute any instance variable initializers and object initializer blocks in textual (left-to-right) order. Don't be confused by the fact that javadoc and javap use alphabetical ordering; that's not important here.
Now execute the remainder of the body for the constructor. This can set instance variables or do anything else.
In general, you have a lot of freedom to choose any of these three forms. My recommendation is to use instance variable initailizers in cases where there is a variable that takes the same value regardless of which constructor is used. Use object initializer blocks only when initialization is complex (e.g. it requires a loop) and you don't want to repeat it in multiple constructors. Use a constructor for the rest.
Here's another example: Program: class A {
String a1 = ABC.echo(" 1: a1");
String a2 = ABC.echo(" 2: a2");
public A() {ABC.echo(" 3: A()");}
}
class B extends A {
String b1 = ABC.echo(" 4: b1");
String b2;
public B() {
ABC.echo(" 5: B()");
b1 = ABC.echo(" 6: b1 reset");
a2 = ABC.echo(" 7: a2 reset");
}
}
class C extends B {
String c1;
{ c1 = ABC.echo(" 8: c1"); }
String c2;
String c3 = ABC.echo(" 9: c3");
public C() {
ABC.echo("10: C()");
c2 = ABC.echo("11: c2");
b2 = ABC.echo("12: b2");
}
}
public class ABC {
static String echo(String arg) {
System.out.println(arg);
return arg;
}
public static void main(String[] args) {
new C();
}
}
Output: 1: a1
2: a2
3: A()
4: b1
5: B()
6: b1 reset
7: a2 reset
8: c1
9: c3
10: C()
11: c2
12: b2
--------------------------------------------------------------------------------
Q: What about class initialization?
It is important to distinguish class initialization from instance creation. An instance is created when you call a constructor with new. A class C is initialized the first time it is actively used. At that time, the initialization code for the class is run, in textual order. There are two kinds of class initialization code: static initializer blocks (static { ... }), and class variable initializers (static String var = ...).
Active use is defined as the first time you do any one of the following:
Create an instance of C by calling a constructor;
Call a static method that is defined in C (not inherited);
Assign or access a static variable that is declared (not inherited) in C. It does not count if the static variable is initialized with a constant expression (one involving only primitive operators (like + or ||), literals, and static final variables), because these are initialized at compile time.
Here is an example:
Program: class A {
static String a1 = ABC.echo(" 1: a1");
static String a2 = ABC.echo(" 2: a2");
}
class B extends A {
static String b1 = ABC.echo(" 3: b1");
static String b2;
static {
ABC.echo(" 4: B()");
b1 = ABC.echo(" 5: b1 reset");
a2 = ABC.echo(" 6: a2 reset");
}
}
class C extends B {
static String c1;
static { c1 = ABC.echo(" 7: c1"); }
static String c2;
static String c3 = ABC.echo(" 8: c3");
static {
ABC.echo(" 9: C()");
c2 = ABC.echo("10: c2");
b2 = ABC.echo("11: b2");
}
}
public class ABC {
static String echo(String arg) {
System.out.println(arg);
return arg;
}
public static void main(String[] args) {
new C();
}
}
Output: 1: a1
2: a2
3: b1
4: B()
5: b1 reset
6: a2 reset
7: c1
8: c3
9: C()
10: c2
11: b2
--------------------------------------------------------------------------------
Q: I have a class with six instance variables, each of which could be initialized or not. Should I write 64 constructors?
Of course you don't need (26) constructors. Let's say you have a class C defined as follows:
public class C { int a,b,c,d,e,f; }
Here are some things you can do for constructors:
Guess at what combinations of variables will likely be wanted, and provide constructors for those combinations. Pro: That's how it's usually done. Con: Difficult to guess correctly; lots of redundant code to write.
Define setters that can be cascaded because they return this. That is, define a setter for each instance variable, then use them after a call to the default constructor:
public C setA(int val) { a = val; return this; }
...
new C().setA(1).setC(3).setE(5);
Pro: This is a reasonably simple and efficient approach. A similar idea is discussed by Bjarne Stroustrop on page 156 of The Design and Evolution of C++. Con: You need to write all the little setters, they aren't JavaBean-compliant (since they return this, not void), they don't work if there are interactions between two values.
Use the default constructor for an anonymous sub-class with a non-static initializer:
new C() {{ a = 1; c = 3; e = 5; }}
Pro: Very concise; no mess with setters. Con: The instance variables can't be private, you have the overhead of a sub-class, your object won't actually have C as its class (although it will still be an instanceof C), it only works if you have accessible instance variables, and many people, including experienced Java programmers, won't understand it. Actually, its quite simple: You are defining a new, unnamed (anonymous) subclass of C, with no new methods or variables, but with an initialization block that initializes a, c, and e. Along with defining this class, you are also making an instance. When I showed this to Guy Steele, he said "heh, heh! That's pretty cute, all right, but I'm not sure I would advocate widespread use..." As usual, Guy is right. (By the way, you can also use this to create and initialize a vector. You know how great it is to create and initialize, say, a String array with new String[] {"one", "two", "three"}. Now with inner classes you can do the same thing for a vector, where previously you thought you'd have to use assignement statements: new Vector(3) {{add("one"); add("two"); add("three")}}.)
You can switch to a language that directly supports this idiom.. For example, C++ has optional arguments. So you can do this:
class C {
public: C(int a=1, int b=2, int c=3, int d=4, int e=5);
}
...
new C(10); // Construct an instance with defaults for b,c,d,e
Common Lisp and Python have keyword arguments as well as optional arguments, so you can do this:
C(a=10, c=30, e=50) # Construct an instance; use defaults for b and d.
--------------------------------------------------------------------------------
Q:When should I use constructors, and when should I use other methods?
The glib answer is to use constructors when you want a new object; that's what the keyword new is for. The infrequent answer is that constructors are often over-used, both in when they are called and in how much they have to do. Here are some points to consider
Modifiers: As we saw in the previous question, one can go overboard in providing too many constructors. It is usually better to minimize the number of constructors, and then provide modifier methods, that do the rest of the initialization. If the modifiers return this, then you can create a useful object in one expression; if not, you will need to use a series of statements. Modifiers are good because often the changes you want to make during construction are also changes you will want to make later, so why duplicate code between constructors and methods.
Factories: Often you want to create something that is an instance of some class or interface, but you either don't care exactly which subclass to create, or you want to defer that decision to runtime. For example, if you are writing a calculator applet, you might wish that you could call new Number(string), and have this return a Double if string is in floating point format, or a Long if string is in integer format. But you can't do that for two reasons: Number is an abstract class, so you can't invoke its constructor directly, and any call to a constructor must return a new instance of that class directly, not of a subclass. A method which returns objects like a constructor but that has more freedom in how the object is made (and what type it is) is called a factory. Java has no built-in support or conventions for factories, but you will want to invent conventions for using them in your code.
Caching and Recycling: A constructor must create a new object. But creating a new object is a fairly expensive operation. Just as in the real world, you can avoid costly garbage collection by recycling. For example, new Boolean(x) creates a new Boolean, but you should almost always use instead (x ? Boolean.TRUE : Boolean.FALSE), which recycles an existing value rather than wastefully creating a new one. Java would have been better off if it advertised a method that did just this, rather than advertising the constructor. Boolean is just one example; you should also consider recycling of other immutable classes, including Character, Integer, and perhaps many of your own classes. Below is an example of a recycling factory for Numbers. If I had my choice, I would call this Number.make, but of course I can't add methods to the Number class, so it will have to go somewhere else.
public Number numberFactory(String str) throws NumberFormatException {
try {
long l = Long.parseLong(str);
if (l >= 0 && l < cachedLongs.length) {
int i = (int)l;
if (cachedLongs[i] != null) return cachedLongs[i];
else return cachedLongs[i] = new Long(str);
} else {
return new Long(l);
}
} catch (NumberFormatException e) {
double d = Double.parseDouble(str);
return d == 0.0 ? ZERO : d == 1.0 ? ONE : new Double(d);
}
}
private Long[] cachedLongs = new Long[100];
private Double ZERO = new Double(0.0);
private Double ONE = new Double(1.0);
We see that new is a useful convention, but that factories and recycling are also useful. Java chose to support only new because it is the simplest possibility, and the Java philosophy is to keep the language itself as simple as possible. But that doesn't mean your class libraries need to stick to the lowest denominator. (And it shouldn't have meant that the built-in libraries stuck to it, but alas, they did.)
--------------------------------------------------------------------------------
Q: Will I get killed by the overhead of object creation and GC?
Suppose the application has to do with manipulating lots of 3D geometric points. The obvious Java way to do it is to have a class Point with doubles for x,y,z coordinates. But allocating and garbage collecting lots of points can indeed cause a performance problem. You can help by managing your own storage in a resource pool. Instead of allocating each point when you need it, you can allocate a large array of Points at the start of the program. The array (wrapped in a class) acts as a factory for Points, but it is a socially-conscious recycling factory. The method call pool.point(x,y,z) takes the first unused Point in the array, sets its 3 fields to the specified values, and marks it as used. Now you as a programmer are responsible for returning Points to the pool once they are no longer needed. There are several ways to do this. The simplest is when you know you will be allocating Points in blocks that are used for a while, and then discarded. Then you do int pos = pool.mark() to mark the current position of the pool. When you are done with the section of code, you call pool.restore(pos) to set the mark back to the position. If there are a few Points that you would like to keep, just allocate them from a different pool. The resource pool saves you from garbage collection costs (if you have a good model of when your objects will be freed) but you still have the initial object creation costs. You can get around that by going "back to Fortran": using arrays of x,y and z coordinates rather than individual point objects. You have a class of Points but no class for an individual point. Consider this resource pool class:
public class PointPool {
/** Allocate a pool of n Points. **/
public PointPool(int n) {
x = new double[n];
y = new double[n];
z = new double[n];
next = 0;
}
public double x[], y[], z[];
/** Initialize the next point, represented as in integer index. **/
int point(double x1, double y1, double z1) {
x[next] = x1; y[next] = y1; z[next] = z1;
return next++;
}
/** Initialize the next point, initilized to zeros. **/
int point() { return point(0.0, 0.0, 0.0); }
/** Initialize the next point as a copy of a point in some pool. **/
int point(PointPool pool, int p) {
return point(pool.x[p], pool.y[p], pool.z[p]);
}
public int next;
}
You would use this class as follows:
PointPool pool = new PointPool(1000000);
PointPool results = new PointPool(100);
...
int pos = pool.next;
doComplexCalculation(...);
pool.next = pos;
...
void doComplexCalculation(...) {
...
int p1 = pool.point(x, y, z);
int p2 = pool.point(p, q, r);
double diff = pool.x[p1] - pool.x[p2];
...
int p_final = results.point(pool,p1);
...
}
Allocating a million points took half a second for the PointPool approach, and 6 seconds for the straightforward approach that allocates a million instances of a Point class, so that's a 12-fold speedup.
Wouldn't it be nice if you could declare p1, p2 and p_final as Point rather than int? In C or C++, you could just do typedef int Point, but Java doesn't allow that. If you're adventurous, you can set up make files to run your files through the C preprocessor before the Java compiler, and then you can do #define Point int.
--------------------------------------------------------------------------------
Q: I have a complex expression inside a loop. For efficiency, I'd like the computation to be done only once. But for readability, I want it to stay inside the loop where it is used. What can I do?
Let's assume an example where match is a regular expression pattern match routine, and compile compiles a string into a finite state machine that can be used by match:
for(;;) {
...
String str = ...
match(str, compile("a*b*c*"));
...
}
Since Java has no macros, and little control over time of execution, your choices are limited here. One possibility, although not very pretty, is to use an inner interface with a variable initializer:
for(;;) {
...
String str = ...
interface P1 {FSA f = compile("a*b*c*);}
match(str, P1.f);
...
}
The value for P1.f gets initialized on the first use of P1, and is not changed, since variables in interfaces are implicitly static final. If you don't like that, you can switch to a language that gives you better control. In Common Lisp, the character sequence #. means to evaluate the following expression at read (compile) time, not run time. So you could write:
(loop
...
(match str #.(compile "a*b*c*"))
...)
----------------------------------------------------------------------------------
Q: Can I get good advice from books on Java?
There are a lot of Java books out there, falling into three classes:
Bad. Most Java books are written by people who couldn't get a job as a Java programmer (since programming almost always pays more than book writing; I know because I've done both). These books are full of errors, bad advice, and bad programs. These books are dangerous to the beginner, but are easily recognized and rejected by a programmer with even a little experience in another language.
Excellent. There are a small number of excellent Java books. I like the official specification and the books by Arnold and Gosling, Marty Hall, and Peter van der Linden. For reference I like the Java in a Nutshell series and the online references at Sun (I copy the javadoc APIs and the language specification and its amendments to my local disk and bookmark them in my browser so I'll always have fast access.)
Iffy. In between these two extremes is a collection of sloppy writing by people who should know better, but either haven't taken the time to really understand how Java works, or are just rushing to get something published fast. One such example of half-truths is Edward Yourdon's Java and the new Internet programming paradigm from Rise and Resurrection of the American Programmer [footnote on Yourdon]. Here's what Yourdon says about how different Java is:
"Functions have been eliminated" It's true that there is no "function" keyword in Java. Java calls them methods (and Perl calls them subroutines, and Scheme calls them procedures, but you wouldn't say these languages have eliminated functions). One could reasonably say that there are no global functions in Java. But I think it would be more precise to say that there are functions with global extent; its just that they must be defined within a class, and are called "static method C.f" instead of "function f".
"Automatic coercions of data types have been eliminated" It's true that there are limits in the coercions that are made, but they are far from eliminated. You can still say (1.0 + 2) and 2 will be automatically coerced to a double. Or you can say ("one" + 2) and 2 will be coerced to a string.
"Pointers and pointer arithmetic have been eliminated" It's true that explicit pointer arithmetic has been eliminated (and good riddance). But pointers remain; in fact, every reference to an object is a pointer. (That's why we have NullPointerException.) It is impossible to be a competent Java programmer without understanding this. Every Java programmer needs to know that when you do:
int[] a = {0, 1, 2};
int[] b = a;
b[0] = 99;
then a[0] is 99 because a and b are pointers (or references) to the same object.
"Because structures are gone, and arrays and strings are represented as objects, the need for pointers has largely disappeared." This is also misleading. First of all, structures aren't gone, they're just renamed "classes". What is gone is programmer control over whether structure/class instances are allocated on the heap or on the stack. In Java all objects are allocated on the heap. That is why there is no need for syntactic markers (such as *) for pointers--if it references an object in Java, it's a pointer. Yourdan is correct in saying that having pointers to the middle of a string or array is considered good idiomatic usage in C and assembly language (and by some people in C++), but it is neither supported nor missed in other languages.
Yourdon also includes a number of minor typos, like saying that arrays have a length() method (instead of a length field) and that modifiable strings are represented by StringClass (instead of StringBuffer). These are annoying, but not as harmful as the more basic half-truths.
(http://www.norvig.com/java-iaq.html#iaq)
Q: What is an Infrequently Answered Question?
A question is infrequently answered either because few people know the answer or because it is about an obscure, subtle point (but a point that may be crucial to you). I thought I had invented the term, but it also shows up at the very informative About.com Urban Legends site. There are lots of Java FAQs around, but this is the only Java IAQ. (There are a few Infrequently Asked Questions lists, including a satirical one on C.)
--------------------------------------------------------------------------------
Q:The code in a finally clause will never fail to execute, right?
Well, hardly ever. But here's an example where the finally code will not execute, regardless of the value of the boolean choice:
try {
if (choice) {
while (true) ;
} else {
System.exit(1);
}
} finally {
code.to.cleanup();
}
--------------------------------------------------------------------------------
Q:Within a method m in a class C, isn't this.getClass() always C?
No. It's possible that for some object x that is an instance of some subclass C1 of C either there is no C1.m() method, or some method on x called super.m(). In either case, this.getClass() is C1, not C within the body of C.m(). If C is final, then you're ok.
--------------------------------------------------------------------------------
Q: I defined an equals method, but Hashtable ignores it. Why?
equals methods are surprisngly hard to get right. Here are the places to look first for a problem:
You defined the wrong equals method. For example, you wrote:
public class C {
public boolean equals(C that) { return id(this) == id(that); }
}
But in order for table.get(c) to work you need to make the equals method take an Object as the argument, not a C:
public class C {
public boolean equals(Object that) {
return (that instanceof C) && id(this) == id((C)that);
}
}
Why? The code for Hashtable.get looks something like this:
public class Hashtable {
public Object get(Object key) {
Object entry;
...
if (entry.equals(key)) ...
}
}
Now the method invoked by entry.equals(key) depends upon the actual run-time type of the object referenced by entry, and the declared, compile-time type of the variable key. So when you as a user call table.get(new C(...)), this looks in class C for the equals method with argument of type Object. If you happen to have defined an equals method with argument of type C, that's irrelevent. It ignores that method, and looks for a method with signature equals(Object), eventually finding Object.equals(Object). If you want to over-ride a method, you need to match argument types exactly. In some cases, you may want to have two methods, so that you don't pay the overhead of casting when you know you have an object of the right class:
public class C {
public boolean equals(Object that) {
return (this == that)
|| ((that instanceof C) && this.equals((C)that));
}
public boolean equals(C that) {
return id(this) == id(that); // Or whatever is appropriate for class C
}
}
You didn't properly implement equals as an equality predicate: equals must be symmetric, transitive, and reflexive. Symmetric means a.equals(b) must have the same value as b.equals(a). (This is the one most people mess up.) Transitive means that if a.equals(b) and b.equals(c) then a.equals(c) must be true. Reflexive means that a.equals(a) must be true, and is the reason for the (this == that) test above (it's also often good practice to include this because of efficiency reasons: testing for == is faster than looking at all the slots of an object, and to partially break the recursion problem on objects that might have circular pointer chains).
You forgot the hashCode method. Anytime you define a equals method, you should also define a hashCode method. You must make sure that two equal objects have the same hashCode, and if you want better hashtable performance, you should try to make most non-equal objects have different hashCodes. Some classes cache the hash code in a private slot of an object, so that it need be computed only once. If that is the case then you will probably save time in equals if you include a line that says if (this.hashSlot != that.hashSlot) return false.
You didn't handle inheritance properly. First of all, consider if two objects of different class can be equal. Before you say "NO! Of course not!" consider a class Rectangle with width and height fields, and a Box class, which has the above two fields plus depth. Is a Box with depth == 0 equal to the equivalent Rectangle? You might want to say yes. If you are dealing with a non-final class, then it is possible that your class might be subclassed, and you will want to be a good citizen with respect to your subclass. In particular, you will want to allow an extender of your class C to use your C.equals method using super as follows:
public class C2 extends C {
int newField = 0;
public boolean equals(Object that) {
if (this == that) return true;
else if (!(that instanceof C2)) return false;
else return this.newField == ((C2)that).newField) && super.equals(that);
}
}
To allow this to work, you have to be careful about how you treat classes in your definition of C.equals. For example, check for that instanceof C rather than that.getClass() == C.class. See the previous IAQ question to learn why. Use this.getClass() == that.getClass() if you are sure that two objects must be of the same class to be considered equals.
You didn't handle circular references properly. Consider:
public class LinkedList {
Object contents;
LinkedList next = null;
public boolean equals(Object that) {
return (this == that)
|| ((that instanceof LinkedList) && this.equals((LinkedList)that));
}
public boolean equals(LinkedList that) { // Buggy!
return Util.equals(this.contents, that.contents) &&
Util.equals(this.next, that.next);
}
}
Here I have assumed there is a Util class with:
public static boolean equals(Object x, Object y) {
return (x == y) || (x != null && x.equals(y));
}
I wish this method were in Object; without it you always have to throw in tests against null. Anyway, the LinkedList.equals method will never return if asked to compare two LinkedLists with circular references in them (a pointer from one element of the linked list back to another element). See the description of the Common Lisp function list-length for an explanation of how to handle this problem in linear time with only two words of extra storge. (I don't give the answer here in case you want to try to figure it out for yourself first.)
--------------------------------------------------------------------------------
Q: I tried to forward a method to super, but it occasionally doesn't work. Why?
This is the code in question, simplified for this example:
/** A version of Hashtable that lets you do
* table.put("dog", "canine");, and then have
* table.get("dogs") return "canine". **/
public class HashtableWithPlurals extends Hashtable {
/** Make the table map both key and key + "s" to value. **/
public Object put(Object key, Object value) {
super.put(key + "s", value);
return super.put(key, value);
}
}
You need to be careful when passing to super that you fully understand what the super method does. In this case, the contract for Hashtable.put is that it will record a mapping between the key and the value in the table. However, if the hashtable gets too full, then Hashtable.put will allocate a larger array for the table, copy all the old objects over, and then recursively re-call table.put(key, value). Now, because Java resolves methods based on the runtime type of the target, in our example this recursive call within the code for Hashtable will go to HashtableWithPlurals.put(key, value), and the net result is that occasionally (when the size of the table overflows at just the wrong time), you will get an entry for "dogss" as well as for "dogs" and "dog". Now, does it state anywhere in the documentation for put that doing this recursive call is a possibility? No. In cases like this, it sure helps to have source code access to the JDK.
--------------------------------------------------------------------------------
Q: Why does my Properties object ignore the defaults when I do a get?
You shouldn't do a get on a Properties object; you should do a getProperty instead. Many people assume that the only difference is that getProperty has a declared return type of String, while get is declared to return an Object. But actually there is a bigger difference: getProperty looks at the defaults. get is inherited from Hashtable, and it ignores the default, thereby doing exactly what is documented in the Hashtable class, but probably not what you expect. Other methods that are inherited from Hashtable (like isEmpty and toString) will also ignore defaults. Example code:
Properties defaults = new Properties();
defaults.put("color", "black");
Properties props = new Properties(defaults);
System.out.println(props.get("color") + ", " +
props.getProperty(color));
// This prints "null, black"
Is this justified by the documentation? Maybe. The documentation in Hashtable talks about entries in the table, and the behavior of Properties is consistent if you assume that defauls are not entries in the table. If for some reason you thought defaults were entries (as you might be led to believe by the behavior of getProperty) then you will be confused.
--------------------------------------------------------------------------------
Q:Inheritance seems error-prone. How can I guard against these errors?
The previous two questions show that a programmer neeeds to be very careful when extending a class, and sometimes just in using a class that extends another class. Problems like these two lead John Ousterhout to say "Implementation inheritance causes the same intertwining and brittleness that have been observed when goto statements are overused. As a result, OO systems often suffer from complexity and lack of reuse." (Scripting, IEEE Computer, March 1998) and Edsger Dijkstra to allegedly say "Object-oriented programming is an exceptionally bad idea which could only have originated in California." (from a collection of signature files). I don't think there's a general way to insure being safe, but there are a few things to be aware of:
Extending a class that you don't have source code for is always risky; the documentation may be incomplete in ways you can't foresee.
Calling super tends to make these unforeseen problems jump out.
You need to pay as much attention to the methods that you don't over-ride as the methods that you do. This is one of the big fallacies of Object-Oriented design using inheritance. It is true that inheritance lets you write less code. But you still have to think about the code you don't write.
You're especially looking for trouble if the subclass changes the contract of any of the methods, or of the class as a whole. It is difficult to tell when a contract is changed, since contracts are informal (there is a formal part in the type signature, but the rest appears only in comments). In the Properties example, it is not clear if a contract is being broken, because it is not clear if the defaults are to be considered "entries" in the table or not.
--------------------------------------------------------------------------------
Q:What are some alternatives to inheritance?
Delegation is an alternative to inheritance. Delegation means that you include an instance of another class as an instance variable, and forward messages to the instance. It is often safer than inheritance because it forces you to think about each message you forward, because the instance is of a known class, rather than a new class, and because it doesn't force you to accept all the methods of the super class: you can provide only the methods that really make sense. On the other hand, it makes you write more code, and it is harder to re-use (because it is not a subclass).
For the HashtableWithPlurals example, delegation would give you this (note: as of JDK 1.2, Dictionary is considered obsolete; use Map instead):
/** A version of Hashtable that lets you do
* table.put("dog", "canine");, and then have
* table.get("dogs") return "canine". **/
public class HashtableWithPlurals extends Dictionary {
Hashtable table = new Hashtable();
/** Make the table map both key and key + "s" to value. **/
public Object put(Object key, Object value) {
table.put(key + "s", value);
return table.put(key, value);
}
... // Need to implement other methods as well
}
The Properties example, if you wanted to enforce the interpretation that default values are entries, would be better done with delegation. Why was it done with inheritance, then? Because the Java implementation team was rushed, and took the course that required writing less code.
--------------------------------------------------------------------------------
Q: Why are there no global variables in Java?
Global variables are considered bad form for a variety of reasons:
Adding state variables breaks referential transparency (you no longer can understand a statement or expression on its own: you need to understand it in the context of the settings of the global variables).
State variables lessen the cohesion of a program: you need to know more to understand how something works. A major point of Object-Oriented programming is to break up global state into more easily understood collections of local state.
When you add one variable, you limit the use of your program to one instance. What you thought was global, someone else might think of as local: they may want to run two copies of your program at once.
For these reasons, Java decided to ban global variables.
--------------------------------------------------------------------------------
Q: I still miss global variables. What can I do instead?
That depends on what you want to do. In each case, you need to decide two things: how many copies of this so-called global variable do I need? And where would be a convenient place to put it? Here are some common solutions:
If you really want only one copy per each time a user invokes Java by starting up a Java virtual machine, then you probably want a static instance variable. For example, you have a MainWindow class in your application, and you want to count the number of windows that the user has opened, and initiate the "Really quit?" dialog when the user has closed the last one. For that, you want: // One variable per class (per JVM)
public Class MainWindow {
static int numWindows = 0;
...
// when opening: MainWindow.numWindows++;
// when closing: MainWindow.numWindows--;
}
In many cases, you really want a class instance variable. For example, suppose you wrote a web browser and wanted to have the history list as a global variable. In Java, it would make more sense to have the history list be an instance variable in the Browser class. Then a user could run two copies of the browser at once, in the same JVM, without having them step on each other. // One variable per instance
public class Browser {
HistoryList history = new HistoryList();
...
// Make entries in this.history
}
Now suppose that you have completed the design and most of the implementation of your browser, and you discover that, deep down in the details of, say, the Cookie class, inside the Http class, you want to display an error message. But you don't know where to display the message. You could easily add an instance variable to the Browser class to hold the display stream or frame, but you haven't passed the current instance of the browser down into the methods in the Cookie class. You don't want to change the signatures of many methods to pass the browser along. You can't use a static variable, because there might be multiple browsers running. However, if you can guarantee that there will be only one browser running per thread (even if each browser may have multiple threads) then there is a good solution: store a table of thread-to-browser mappings as a static variable in the Browser class, and look up the right browser (and hence display) to use via the current thread: // One "variable" per thread
public class Browser {
static Hashtable browsers = new Hashtable();
public Browser() { // Constructor
browsers.put(Thread.currentThread(), this);
}
...
public void reportError(String message) {
Thread t = Thread.currentThread();
((Browser)Browser.browsers.get(t))
.show(message)
}
}
Finally, suppose you want the value of a global variable to persist between invocations of the JVM, or to be shared among multiple JVMs in a network of machines. Then you probably should use a database which you access through JDBC, or you should serialize data and write it to a file.
--------------------------------------------------------------------------------
Q: Can I write sin(x) instead of Math.sin(x)?
Short answer: no. Get used to writing the class name to access static methods from outside the class. However, if you insist on a longer answer ...
If you only want a few methods, you can put in calls to them within your own class: public static double sin(double x) { return Math.sin(x); }
public static double cos(double x) { return Math.cos(x); }
...
sin(x)
Static methods take a target (thing to the left of the dot) that is either a class name, or is an object whose value is ignored, but must be declared to be of the right class. So you could save three characters per call by doing: // Can't instantiate Math, so it must be null.
Math m = null;
...
m.sin(x)
java.lang.Math is a final class, so you can't inherit from it, but if you have your own set of static methods that you would like to share among many of your own classes, then you can package them up and inherit them: public abstract class MyStaticMethods {
public static double mysin(double x) { ... }
}
public class MyClass1 extends MyStaticMethods {
...
mysin(x)
}
Peter van der Linden, author of Just Java, recommends against both of the last two practices in his FAQ. I agree with him that Math m = null is a bad idea in most cases, but I'm not convinced that the MyStaticMethods demonstrates "very poor OOP style to use inheritance to obtain a trivial name abbreviation (rather than to express a type hierarchy)." First of all, trivial is in the eye of the beholder; the abbreviation may be substantial. (See an example of how I used this approach to what I thought was good effect.) Second, it is rather presumptuous to say that this is very bad OOP style. You could make a case that it is bad Java style, but in languages with multiple inheritance, this idiom would be more acceptable.
Another way of looking at it is that features of Java (and any language) necessarily involve trade-offs, and conflate many issues. I agree it is bad to use inheritance in such a way that you mislead the user into thinking that MyClass1 is inheriting behavior from MyStaticMethods, and it is bad to prohibit MyClass1 from extending whatever other class it really wants to extend. But in Java the class is also the unit of encapsulation, compilation (mostly), and name scope. The MyStaticMethod approach scores negative points on the type hierarchy front, but positive points on the name scope front. If you say that the type hierarchy view is more important, I won't argue with you. But I will argue if you think of a class as doing only one thing, rather than many things at once, and if you think of style guides as absolute rather than as trade-offs.
--------------------------------------------------------------------------------
Q: Is null an Object?
Absolutely not. By that, I mean (null instanceof Object) is false. Some other things you should know about null:
You can't call a method on null: x.m() is an error when x is null and m is a non-static method. (When m is a static method it is fine, because it is the class of x that matters; the value is ignored.)
There is only one null, not one for each class. Thus, ((String) null == (Hashtable) null), for example.
It is ok to pass null as an argument to a method, as long as the method is expecting it. Some methods do; some do not. So, for example, System.out.println(null) is ok, but string.compareTo(null) is not. For methods you write, your javadoc comments should say whether null is ok, unless it is obvious.
In JDK 1.1 to 1.1.5, passing null as the literal argument to a constructor of an anonymous inner class (e.g., new SomeClass(null) { ...} caused a compiler error. It's ok to pass an expression whose value is null, or to pass a coerced null, like new SomeClass((String) null) { ...}
There are at least three different meanings that null is commonly used to express:
Uninitialized. A variable or slot that hasn't yet been assigned its real value.
Non-existant/not applicable. For example, terminal nodes in a binary tree might be represented by a regular node with null child pointers.
Empty. For example, you might use null to represent the empty tree. Note that this is subtly different from the previous case, although some people make the mistake of confusing the two cases. The difference is whether null is an acceptable tree node, or whether it is a signal to not treat the value as a tree node. Compare the following three implementations of binary tree nodes with an in-order print method:
// null means not applicable
// There is no empty tree.
class Node {
Object data;
Node left, right;
void print() {
if (left != null)
left.print();
System.out.println(data);
if (right != null)
right.print();
}
}
// null means empty tree
// Note static, non-static methods
class Node {
Object data;
Node left, right;
void static print(Node node) {
if (node != null) node.print();
}
void print() {
print(left);
System.out.println(data);
print(right);
}
}
// Separate class for Empty
// null is never used
interface Node { void print(); }
class DataNode implements Node{
Object data;
Node left, right;
void print() {
left.print();
System.out.println(data);
right.print();
}
}
class EmptyNode implements Node {
void print() { }
}
--------------------------------------------------------------------------------
Q: How big is an Object? Why is there no sizeof?
C has a sizeof operator, and it needs to have one, because the user has to manage calls to malloc, and because the size of primitive types (like long) is not standardized. Java doesn't need a sizeof, but it would still have been a convenient aid. Since it's not there, you can do this:
static Runtime runtime = Runtime.getRuntime();
...
long start, end;
Object obj;
runtime.gc();
start = runtime.freememory();
obj = new Object(); // Or whatever you want to look at
end = runtime.freememory();
System.out.println("That took " + (start-end) + "
bytes.");
This method is not foolproof, because a garbage collection could occur in the middle of the code you are instrumenting, throwing off the byte count. Also, if you are using a just-in-time compiler, some bytes may come from generating code.
You might be surprised to find that an Object takes 16 bytes, or 4 words, in the Sun JDK VM. This breaks down as follows: There is a two-word header, where one word is a pointer to the object's class, and the other points to the instance variables. Even though Object has no instance variables, Java still allocates one word for the variables. Finally, there is a "handle", which is another pointer to the two-word header. Sun says that this extra level of indirection makes garbage collection simpler. (There have been high performance Lisp and Smalltalk garbage collectors that do not use the extra level for at least 15 years. I have heard but have not confirmed that the Microsoft JVM does not have the extra level of indirection.)
An empty new String() takes 40 bytes, or 10 words: 3 words of pointer overhead, 3 words for the instance variables (the start index, end index, and character array), and 4 words for the empty char array. Creating a substring of an existing string takes "only" 6 words, because the char array is shared. Putting an Integer key and Integer value into a Hashtable takes 64 bytes (in addition to the four bytes that were pre-allocated in the Hashtable array): I'll let you work out why.
--------------------------------------------------------------------------------
Q: In what order is initialization code executed? What should I put where?
Instance variable initialization code can go in three places within a class:
In an instance variable initializer for a class (or a superclass). class C {
String var = "val";
In a constructor for a class (or a superclass). public C() { var = "val"; }
In an object initializer block. This is new in Java 1.1; its just like a static initializer block but without the keyword static. { var = "val"; }
}
The order of evaluation (ignoring out of memory problems) when you say new C() is:
Call a constructor for C's superclass (unless C is Object, in which case it has no superclass). It will always be the no-argument constructor, unless the programmer explicitly coded super(...) as the very first statement of the constructor.
Once the super constructor has returned, execute any instance variable initializers and object initializer blocks in textual (left-to-right) order. Don't be confused by the fact that javadoc and javap use alphabetical ordering; that's not important here.
Now execute the remainder of the body for the constructor. This can set instance variables or do anything else.
In general, you have a lot of freedom to choose any of these three forms. My recommendation is to use instance variable initailizers in cases where there is a variable that takes the same value regardless of which constructor is used. Use object initializer blocks only when initialization is complex (e.g. it requires a loop) and you don't want to repeat it in multiple constructors. Use a constructor for the rest.
Here's another example: Program: class A {
String a1 = ABC.echo(" 1: a1");
String a2 = ABC.echo(" 2: a2");
public A() {ABC.echo(" 3: A()");}
}
class B extends A {
String b1 = ABC.echo(" 4: b1");
String b2;
public B() {
ABC.echo(" 5: B()");
b1 = ABC.echo(" 6: b1 reset");
a2 = ABC.echo(" 7: a2 reset");
}
}
class C extends B {
String c1;
{ c1 = ABC.echo(" 8: c1"); }
String c2;
String c3 = ABC.echo(" 9: c3");
public C() {
ABC.echo("10: C()");
c2 = ABC.echo("11: c2");
b2 = ABC.echo("12: b2");
}
}
public class ABC {
static String echo(String arg) {
System.out.println(arg);
return arg;
}
public static void main(String[] args) {
new C();
}
}
Output: 1: a1
2: a2
3: A()
4: b1
5: B()
6: b1 reset
7: a2 reset
8: c1
9: c3
10: C()
11: c2
12: b2
--------------------------------------------------------------------------------
Q: What about class initialization?
It is important to distinguish class initialization from instance creation. An instance is created when you call a constructor with new. A class C is initialized the first time it is actively used. At that time, the initialization code for the class is run, in textual order. There are two kinds of class initialization code: static initializer blocks (static { ... }), and class variable initializers (static String var = ...).
Active use is defined as the first time you do any one of the following:
Create an instance of C by calling a constructor;
Call a static method that is defined in C (not inherited);
Assign or access a static variable that is declared (not inherited) in C. It does not count if the static variable is initialized with a constant expression (one involving only primitive operators (like + or ||), literals, and static final variables), because these are initialized at compile time.
Here is an example:
Program: class A {
static String a1 = ABC.echo(" 1: a1");
static String a2 = ABC.echo(" 2: a2");
}
class B extends A {
static String b1 = ABC.echo(" 3: b1");
static String b2;
static {
ABC.echo(" 4: B()");
b1 = ABC.echo(" 5: b1 reset");
a2 = ABC.echo(" 6: a2 reset");
}
}
class C extends B {
static String c1;
static { c1 = ABC.echo(" 7: c1"); }
static String c2;
static String c3 = ABC.echo(" 8: c3");
static {
ABC.echo(" 9: C()");
c2 = ABC.echo("10: c2");
b2 = ABC.echo("11: b2");
}
}
public class ABC {
static String echo(String arg) {
System.out.println(arg);
return arg;
}
public static void main(String[] args) {
new C();
}
}
Output: 1: a1
2: a2
3: b1
4: B()
5: b1 reset
6: a2 reset
7: c1
8: c3
9: C()
10: c2
11: b2
--------------------------------------------------------------------------------
Q: I have a class with six instance variables, each of which could be initialized or not. Should I write 64 constructors?
Of course you don't need (26) constructors. Let's say you have a class C defined as follows:
public class C { int a,b,c,d,e,f; }
Here are some things you can do for constructors:
Guess at what combinations of variables will likely be wanted, and provide constructors for those combinations. Pro: That's how it's usually done. Con: Difficult to guess correctly; lots of redundant code to write.
Define setters that can be cascaded because they return this. That is, define a setter for each instance variable, then use them after a call to the default constructor:
public C setA(int val) { a = val; return this; }
...
new C().setA(1).setC(3).setE(5);
Pro: This is a reasonably simple and efficient approach. A similar idea is discussed by Bjarne Stroustrop on page 156 of The Design and Evolution of C++. Con: You need to write all the little setters, they aren't JavaBean-compliant (since they return this, not void), they don't work if there are interactions between two values.
Use the default constructor for an anonymous sub-class with a non-static initializer:
new C() {{ a = 1; c = 3; e = 5; }}
Pro: Very concise; no mess with setters. Con: The instance variables can't be private, you have the overhead of a sub-class, your object won't actually have C as its class (although it will still be an instanceof C), it only works if you have accessible instance variables, and many people, including experienced Java programmers, won't understand it. Actually, its quite simple: You are defining a new, unnamed (anonymous) subclass of C, with no new methods or variables, but with an initialization block that initializes a, c, and e. Along with defining this class, you are also making an instance. When I showed this to Guy Steele, he said "heh, heh! That's pretty cute, all right, but I'm not sure I would advocate widespread use..." As usual, Guy is right. (By the way, you can also use this to create and initialize a vector. You know how great it is to create and initialize, say, a String array with new String[] {"one", "two", "three"}. Now with inner classes you can do the same thing for a vector, where previously you thought you'd have to use assignement statements: new Vector(3) {{add("one"); add("two"); add("three")}}.)
You can switch to a language that directly supports this idiom.. For example, C++ has optional arguments. So you can do this:
class C {
public: C(int a=1, int b=2, int c=3, int d=4, int e=5);
}
...
new C(10); // Construct an instance with defaults for b,c,d,e
Common Lisp and Python have keyword arguments as well as optional arguments, so you can do this:
C(a=10, c=30, e=50) # Construct an instance; use defaults for b and d.
--------------------------------------------------------------------------------
Q:When should I use constructors, and when should I use other methods?
The glib answer is to use constructors when you want a new object; that's what the keyword new is for. The infrequent answer is that constructors are often over-used, both in when they are called and in how much they have to do. Here are some points to consider
Modifiers: As we saw in the previous question, one can go overboard in providing too many constructors. It is usually better to minimize the number of constructors, and then provide modifier methods, that do the rest of the initialization. If the modifiers return this, then you can create a useful object in one expression; if not, you will need to use a series of statements. Modifiers are good because often the changes you want to make during construction are also changes you will want to make later, so why duplicate code between constructors and methods.
Factories: Often you want to create something that is an instance of some class or interface, but you either don't care exactly which subclass to create, or you want to defer that decision to runtime. For example, if you are writing a calculator applet, you might wish that you could call new Number(string), and have this return a Double if string is in floating point format, or a Long if string is in integer format. But you can't do that for two reasons: Number is an abstract class, so you can't invoke its constructor directly, and any call to a constructor must return a new instance of that class directly, not of a subclass. A method which returns objects like a constructor but that has more freedom in how the object is made (and what type it is) is called a factory. Java has no built-in support or conventions for factories, but you will want to invent conventions for using them in your code.
Caching and Recycling: A constructor must create a new object. But creating a new object is a fairly expensive operation. Just as in the real world, you can avoid costly garbage collection by recycling. For example, new Boolean(x) creates a new Boolean, but you should almost always use instead (x ? Boolean.TRUE : Boolean.FALSE), which recycles an existing value rather than wastefully creating a new one. Java would have been better off if it advertised a method that did just this, rather than advertising the constructor. Boolean is just one example; you should also consider recycling of other immutable classes, including Character, Integer, and perhaps many of your own classes. Below is an example of a recycling factory for Numbers. If I had my choice, I would call this Number.make, but of course I can't add methods to the Number class, so it will have to go somewhere else.
public Number numberFactory(String str) throws NumberFormatException {
try {
long l = Long.parseLong(str);
if (l >= 0 && l < cachedLongs.length) {
int i = (int)l;
if (cachedLongs[i] != null) return cachedLongs[i];
else return cachedLongs[i] = new Long(str);
} else {
return new Long(l);
}
} catch (NumberFormatException e) {
double d = Double.parseDouble(str);
return d == 0.0 ? ZERO : d == 1.0 ? ONE : new Double(d);
}
}
private Long[] cachedLongs = new Long[100];
private Double ZERO = new Double(0.0);
private Double ONE = new Double(1.0);
We see that new is a useful convention, but that factories and recycling are also useful. Java chose to support only new because it is the simplest possibility, and the Java philosophy is to keep the language itself as simple as possible. But that doesn't mean your class libraries need to stick to the lowest denominator. (And it shouldn't have meant that the built-in libraries stuck to it, but alas, they did.)
--------------------------------------------------------------------------------
Q: Will I get killed by the overhead of object creation and GC?
Suppose the application has to do with manipulating lots of 3D geometric points. The obvious Java way to do it is to have a class Point with doubles for x,y,z coordinates. But allocating and garbage collecting lots of points can indeed cause a performance problem. You can help by managing your own storage in a resource pool. Instead of allocating each point when you need it, you can allocate a large array of Points at the start of the program. The array (wrapped in a class) acts as a factory for Points, but it is a socially-conscious recycling factory. The method call pool.point(x,y,z) takes the first unused Point in the array, sets its 3 fields to the specified values, and marks it as used. Now you as a programmer are responsible for returning Points to the pool once they are no longer needed. There are several ways to do this. The simplest is when you know you will be allocating Points in blocks that are used for a while, and then discarded. Then you do int pos = pool.mark() to mark the current position of the pool. When you are done with the section of code, you call pool.restore(pos) to set the mark back to the position. If there are a few Points that you would like to keep, just allocate them from a different pool. The resource pool saves you from garbage collection costs (if you have a good model of when your objects will be freed) but you still have the initial object creation costs. You can get around that by going "back to Fortran": using arrays of x,y and z coordinates rather than individual point objects. You have a class of Points but no class for an individual point. Consider this resource pool class:
public class PointPool {
/** Allocate a pool of n Points. **/
public PointPool(int n) {
x = new double[n];
y = new double[n];
z = new double[n];
next = 0;
}
public double x[], y[], z[];
/** Initialize the next point, represented as in integer index. **/
int point(double x1, double y1, double z1) {
x[next] = x1; y[next] = y1; z[next] = z1;
return next++;
}
/** Initialize the next point, initilized to zeros. **/
int point() { return point(0.0, 0.0, 0.0); }
/** Initialize the next point as a copy of a point in some pool. **/
int point(PointPool pool, int p) {
return point(pool.x[p], pool.y[p], pool.z[p]);
}
public int next;
}
You would use this class as follows:
PointPool pool = new PointPool(1000000);
PointPool results = new PointPool(100);
...
int pos = pool.next;
doComplexCalculation(...);
pool.next = pos;
...
void doComplexCalculation(...) {
...
int p1 = pool.point(x, y, z);
int p2 = pool.point(p, q, r);
double diff = pool.x[p1] - pool.x[p2];
...
int p_final = results.point(pool,p1);
...
}
Allocating a million points took half a second for the PointPool approach, and 6 seconds for the straightforward approach that allocates a million instances of a Point class, so that's a 12-fold speedup.
Wouldn't it be nice if you could declare p1, p2 and p_final as Point rather than int? In C or C++, you could just do typedef int Point, but Java doesn't allow that. If you're adventurous, you can set up make files to run your files through the C preprocessor before the Java compiler, and then you can do #define Point int.
--------------------------------------------------------------------------------
Q: I have a complex expression inside a loop. For efficiency, I'd like the computation to be done only once. But for readability, I want it to stay inside the loop where it is used. What can I do?
Let's assume an example where match is a regular expression pattern match routine, and compile compiles a string into a finite state machine that can be used by match:
for(;;) {
...
String str = ...
match(str, compile("a*b*c*"));
...
}
Since Java has no macros, and little control over time of execution, your choices are limited here. One possibility, although not very pretty, is to use an inner interface with a variable initializer:
for(;;) {
...
String str = ...
interface P1 {FSA f = compile("a*b*c*);}
match(str, P1.f);
...
}
The value for P1.f gets initialized on the first use of P1, and is not changed, since variables in interfaces are implicitly static final. If you don't like that, you can switch to a language that gives you better control. In Common Lisp, the character sequence #. means to evaluate the following expression at read (compile) time, not run time. So you could write:
(loop
...
(match str #.(compile "a*b*c*"))
...)
----------------------------------------------------------------------------------
Q: Can I get good advice from books on Java?
There are a lot of Java books out there, falling into three classes:
Bad. Most Java books are written by people who couldn't get a job as a Java programmer (since programming almost always pays more than book writing; I know because I've done both). These books are full of errors, bad advice, and bad programs. These books are dangerous to the beginner, but are easily recognized and rejected by a programmer with even a little experience in another language.
Excellent. There are a small number of excellent Java books. I like the official specification and the books by Arnold and Gosling, Marty Hall, and Peter van der Linden. For reference I like the Java in a Nutshell series and the online references at Sun (I copy the javadoc APIs and the language specification and its amendments to my local disk and bookmark them in my browser so I'll always have fast access.)
Iffy. In between these two extremes is a collection of sloppy writing by people who should know better, but either haven't taken the time to really understand how Java works, or are just rushing to get something published fast. One such example of half-truths is Edward Yourdon's Java and the new Internet programming paradigm from Rise and Resurrection of the American Programmer [footnote on Yourdon]. Here's what Yourdon says about how different Java is:
"Functions have been eliminated" It's true that there is no "function" keyword in Java. Java calls them methods (and Perl calls them subroutines, and Scheme calls them procedures, but you wouldn't say these languages have eliminated functions). One could reasonably say that there are no global functions in Java. But I think it would be more precise to say that there are functions with global extent; its just that they must be defined within a class, and are called "static method C.f" instead of "function f".
"Automatic coercions of data types have been eliminated" It's true that there are limits in the coercions that are made, but they are far from eliminated. You can still say (1.0 + 2) and 2 will be automatically coerced to a double. Or you can say ("one" + 2) and 2 will be coerced to a string.
"Pointers and pointer arithmetic have been eliminated" It's true that explicit pointer arithmetic has been eliminated (and good riddance). But pointers remain; in fact, every reference to an object is a pointer. (That's why we have NullPointerException.) It is impossible to be a competent Java programmer without understanding this. Every Java programmer needs to know that when you do:
int[] a = {0, 1, 2};
int[] b = a;
b[0] = 99;
then a[0] is 99 because a and b are pointers (or references) to the same object.
"Because structures are gone, and arrays and strings are represented as objects, the need for pointers has largely disappeared." This is also misleading. First of all, structures aren't gone, they're just renamed "classes". What is gone is programmer control over whether structure/class instances are allocated on the heap or on the stack. In Java all objects are allocated on the heap. That is why there is no need for syntactic markers (such as *) for pointers--if it references an object in Java, it's a pointer. Yourdan is correct in saying that having pointers to the middle of a string or array is considered good idiomatic usage in C and assembly language (and by some people in C++), but it is neither supported nor missed in other languages.
Yourdon also includes a number of minor typos, like saying that arrays have a length() method (instead of a length field) and that modifiable strings are represented by StringClass (instead of StringBuffer). These are annoying, but not as harmful as the more basic half-truths.
(http://www.norvig.com/java-iaq.html#iaq)
Subscribe to:
Posts (Atom)