Java in a Nutshell

Previous Chapter 5
Inner Classes and Other New Language Features

5.6 Other New Features of Java 1.1

While the addition of inner classes is by far the most important and far-reaching change to the Java language in Java 1.1, there have been several other changes to the language as well. They are:

As you can see, the first two items in this list are language changes that are related to, though not exclusively used by, the inner class changes. We covered final local variables and parameters in our discussion of local classes above. And we covered instance initializers in the discussion of anonymous classes. The following subsections discuss the remaining three changes.

Blank Finals

We've already seen that local variables, method parameters, and exception parameters of catch statements may be declared final. A related change is that final fields do not require initializers. In Java 1.0, any final field had to be initialized as part of the field declaration. In Java 1.1, this restriction has been relaxed. A field or local variable can be declared final without specifying an intial value as part of the declaration. These "blank finals," as they are called, must have a value assigned to them before they are ever used, of course. And, once a value has been assigned to a blank final, that value can never be changed. This allows you, for example, to use an instance initializer or a constructor to compute a value for a final field.

Blank finals are particularly useful in defining immutable data types. They allow a class to have immutable fields that are initialized based on run-time arguments to a constructor. Once assigned, these fields cannot be accidentally or maliciously changed.

Anonymous Arrays

In Java 1.0, you can create and initialize an array with code like the following:

int[] a = {1, 2, 3, 4, 5};

Unfortunately, this syntax is only allowed in initializer expressions that follow the declaration of a field or variable of array type. That is, you cannot write code like this:

int[] a;
a = {1, 2, 3, 4, 5};           // Error
int total = sum({1,2,3,4,5});  // Error

You cannot write code like that in Java 1.1 either, but you can write code using a similar array initializer syntax. When you use the new operator to create an array, you may omit the dimension that specifies the number of array elements to create and instead follow the empty square bracket pair ([]) with a list of initial values in curly braces. Such an expression creates an array large enough to hold all of the elements specified between the braces, and initializes the array to contain those elements. The elements in braces must all be of the type specified after the new keyword, of course.

Code that uses anonymous arrays looks like this:

int[] a;
a = new int[] {1, 2, 3, 4, 5};
int total = sum(new int[] {1, 2, 3, 4, 5});
System.out.println(new char[] {'h', 'e', 'l', 'l', 'o'});

As you can see, this new syntax allows you to create and initialize arrays without using a variable initializer, or without even assigning the array to a variable at all. That is why arrays created and initialized this way are called anonymous arrays.

Class Literals

Another major change in Java 1.1 is the introduction of the Reflection API in the java.lang.reflect package. As part of this new package, the java.lang.Class class has been broadened to represent not just Java classes, but all Java data types. In other words, there are now special Class objects that represent each of the Java primitive types.

You can access these special Class objects through the TYPE field of each of the primitive wrapper classes. For example, the static variable Boolean.TYPE holds the Class object that represents the boolean data type. And the Float.TYPE static variable holds the Class object that represents the float data type. A new class Void has been added, and Void.TYPE represents the type void.

The changes described in the paragraph above are all changes to the Java class libraries, rather than changes to the Java language itself. The language change is a related one, however. In Java 1.1, you can obtain the Class object for any class or primitive type by following the class name or type name by a period and the class keyword. For example, String.class evaluates to the Class object that represents the java.lang.String class. Similarly, int.class evaluates to the special class object Integer.TYPE that represents the int data type.

In Java 1.0, it is much more cumbersome (and less efficient) to obtain a Class object--you have to use the static Class.forName() method, so you end up with expressions like:

Class c = Class.forName("java.util.Vector");

Where in Java 1.1 you can simply write:

Class c = java.util.Vector.class;

Remember that class is a keyword in Java, so this syntax does not simply constitute a reference to a static variable pre-defined in each class.

This new syntax is meant to simplify use of the new reflection facilities in Java 1.1. It is also necessary because using Class.forName() with inner classes requires knowledge of the way the compiler transforms the names of inner classes (i.e., where it replaces "." with "$"). While compiler writers need to know about these transformation rules, Java programmers should not. Thus the new .class syntax provides a way to obtain a Class object that works with inner classes, as well as with top-level classes and interfaces.

Previous Home Next
Anonymous Classes Book Index Applets

Java in a Nutshell Java Language Reference Java AWT Java Fundamental Classes Exploring Java