Exploring Advanced Language Features
Chapters 1 and 2 introduced you to Java’s fundamental language features along with its support for classes and objects. Chapter 3 builds onto this foundation by introducing you to Java’s advanced language features, specifically those features related to nested types, packages, static imports, exceptions, assertions, annotations, generics, and enums.
Nested Types
Classes that are declared outside of any class are known as top-level classes. Java also supports nested classes, which are classes declared as members of other classes or scopes. Nested classes help you implement top-level class architecture.
There are four kinds of nested classes: static member classes, nonstatic member classes, anonymous classes, and local classes. The latter three categories are known as inner classes.
This section introduces you to static member classes and inner classes. For each kind of nested class, I provide you with a brief introduction, an abstract example, and a more practical example. The section then briefly examines the topic of nesting interfaces within classes.
Static Member Classes
A static member class is a static member of an enclosing class. Although enclosed, it does not have an enclosing instance of that class, and cannot access the enclosing class’s instance fields and invoke its instance methods. However, it can access the enclosing class’s static fields and invoke its static methods, even those members that are declared private. Listing 3-1 presents a static member class declaration.
Listing 3-1. Declaring a static member class class EnclosingClass
{
private static int i;
private static void m1() {
System.out.println(i);
}
static void m2()
CHAPTER 3 EXPLORING ADVANCED LANGUAGE FEATURES
{
EnclosedClass.accessEnclosingClass();
}
static class EnclosedClass {
static void accessEnclosingClass() {
i = 1;
m1();
}
void accessEnclosingClass2() {
m2();
} } }
Listing 3-1 declares a top-level class named EnclosingClass with class field i, class methods m1() and m2(), and static member class EnclosedClass. Also, EnclosedClass declares class method
accessEnclosingClass() and instance method accessEnclosingClass2().
Because accessEnclosingClass() is declared static, m2() must prefix this method’s name with EnclosedClass and the member access operator to invoke this method.
Listing 3-2 presents the source code to an application that demonstrates how to invoke
EnclosedClass’s accessEnclosingClass() class method, and instantiate EnclosedClass and invoke its accessEnclosingClass2() instance method.
Listing 3-2. Invoking a static member class’s class and instance methods class SMCDemo
{
public static void main(String[] args) {
EnclosingClass.EnclosedClass.accessEnclosingClass(); // Output: 1 EnclosingClass.EnclosedClass ec = new EnclosingClass.EnclosedClass();
ec.accessEnclosingClass2(); // Output: 1 }
}
Listing 3-2’s main() method reveals that you must prefix the name of an enclosed class with the name of its enclosing class to invoke a class method; for example,
EnclosingClass.EnclosedClass.accessEnclosingClass();.
This listing also reveals that you must prefix the name of the enclosed class with the name of its enclosing class when instantiating the enclosed class; for example, EnclosingClass.EnclosedClass ec = new EnclosingClass.EnclosedClass();. You can then invoke the instance method in the normal manner;
for example, ec.accessEnclosingClass2();.
Static member classes have their uses. For example, Listing 3-3’s Double and Float static member classes provide different implementations of their enclosing Rectangle class. The Float version occupies less memory because of its 32-bit float fields, and the Double version provides greater accuracy because of its 64-bit double fields.
CHAPTER 3 EXPLORING ADVANCED LANGUAGE FEATURES
Listing 3-3. Using static member classes to declare multiple implementations of their enclosing class abstract class Rectangle
{
abstract double getX();
abstract double getY();
abstract double getWidth();
abstract double getHeight();
static class Double extends Rectangle {
static class Float extends Rectangle {
// Prevent subclassing. Use the type-specific Double and Float // implementation subclass classes to instantiate.
private Rectangle() {}
boolean contains(double x, double y) {
return (x >= getX() && x < getX()+getWidth()) &&
(y >= getY() && y < getY()+getHeight());
} }
Listing 3-3’s Rectangle class demonstrates nested subclasses. Each of the Double and Float static member classes subclass the abstract Rectangle class, providing private floating-point or double
CHAPTER 3 EXPLORING ADVANCED LANGUAGE FEATURES
precision floating-point fields, and overriding Rectangle’s abstract methods to return these fields’ values as doubles.
Rectangle is abstract because it makes no sense to instantiate this class. Because it also makes no sense to directly extend Rectangle with new implementations (the Double and Float nested subclasses should be sufficient), its default constructor is declared private. Instead, you must instantiate
Rectangle.Float (to save memory) or Rectangle.Double (when accuracy is required), as demonstrated by Listing 3-4.
Listing 3-4. Instantiating nested subclasses class SMCDemo
{
public static void main(String[] args) {
Rectangle r = new Rectangle.Double(10.0, 10.0, 20.0, 30.0);
System.out.println("x = "+r.getX());
System.out.println("y = "+r.getY());
System.out.println("width = "+r.getWidth());
System.out.println("height = "+r.getHeight());
System.out.println("contains(15.0, 15.0) = "+r.contains(15.0, 15.0));
System.out.println("contains(0.0, 0.0) = "+r.contains(0.0, 0.0));
System.out.println();
r = new Rectangle.Float(10.0f, 10.0f, 20.0f, 30.0f);
System.out.println("x = "+r.getX());
System.out.println("y = "+r.getY());
System.out.println("width = "+r.getWidth());
System.out.println("height = "+r.getHeight());
System.out.println("contains(15.0, 15.0) = "+r.contains(15.0, 15.0));
System.out.println("contains(0.0, 0.0) = "+r.contains(0.0, 0.0));
} }
Listing 3-4 first instantiates Rectangle’s Double subclass via new Rectangle.Double(10.0, 10.0, 20.0, 30.0) and then invokes its various methods. Continuing, Listing 3-4 instantiates Rectangle’s Float subclass via new Rectangle.Float(10.0f, 10.0f, 20.0f, 30.0f) before invoking Rectangle methods on this instance.
Compile both listings (javac SMCDemo.java or javac *.java) and run the application (java SMCDemo).
You will then observe the following output:
x = 10.0 y = 10.0 width = 20.0 height = 30.0
contains(15.0, 15.0) = true contains(0.0, 0.0) = false x = 10.0
y = 10.0 width = 20.0 height = 30.0
contains(15.0, 15.0) = true contains(0.0, 0.0) = false
CHAPTER 3 EXPLORING ADVANCED LANGUAGE FEATURES
Java’s class library contains many static member classes. For example, the java.lang.Character class encloses a static member class named Subset whose instances represent subsets of the Unicode character set. java.util.AbstractMap.SimpleEntry, java.io.ObjectInputStream.GetField, and java.security.KeyStore.PrivateKeyEntry are other examples.
■ Note When you compile an enclosing class that contains a static member class, the compiler creates a classfile
for the static member class whose name consists of its enclosing class’s name, a dollar-sign character, and the static member class’s name. For example, compile Listing 3-1 and you will discover
EnclosingClass$EnclosedClass.class