ONJava.com -- The Independent Source for Enterprise Java
oreilly.comSafari Books Online.Conferences.

advertisement

AddThis Social Bookmark Button O'Reilly Book Excerpts: Hardcore Java

Nested Classes, Part 3

by Robert Simmons, Jr.

Editor's note: Robert Simmons concludes his quest to clarify the uses, advantages, and pitfalls to using Java's various nested classes with this week's third installment on the subject, excerpted from his book Hardcore Java. Robert covers static nested classes and how they differ from inner classes; double nested classes and whether and when to use them; and nested classes and interfaces and their usefulness, or lack thereof. (If you missed either of the first two installments in this series, you'll find links to those excerpts on these pages as well.)

Hardcore Java

Related Reading

Hardcore Java
By Robert Simmons, Jr.

Static Nested Classes

A static nested class is, not surprisingly,a nested class that is declared with the keyword static on the declaration. Many developers call these classes "inner classes" as well. However, these developers are wrong. To be an inner class, a class has to have instance scope. To understand how static nested classes function, see Example 6-8.

Example 6-8. A static nested class

package oreilly.hcj.nested;
public class OuterClass {

   private static final String name = "Robert";
   private static String company = "O'Reilly";
   private int value = 5;

   public static class SomeClass {
      public void someMethod( ) {
         System.out.println(company);
         System.out.println(name);
         System.out.println(value); // <= Compiler error
      }
   }
}

This code declares a public static nested class. This nested class can access the various static members of the enclosing class. However, unlike inner classes, it cannot access any instance variables in the enclosing class. This is an important difference between static nested classes and inner classes. Formally, it can be said that an inner class is instance-scoped and a static nested class is class-scoped. The class scope of a static nested class makes it easier to use:

package oreilly.hcj.nested;
public class StaticNestedClassDemo {
   public static void main(final String[] args) {
      OuterClass.SomeClass obj = new OuterClass.SomeClass( );
      obj.someMethod( );
   }
}

To instantiate a public static nested class,you do not need an instance of the outer class. You can simply use the class as is to create new instances:

import oreilly.hcj.nested.OuterClass.SomeClass;
public class StaticNestedClassDemo {
   public static void main(final String[] args) {
      SomeClass obj = new SomeClass( );
      obj.someMethod( );
   }
}

With this import syntax,the class is quite easy to use. However,static nested classes can also use access specifiers. You can have public, private, protected, package, final, and even abstract static nested classes. If you try to access a static nested class outside of its visibility, your code won't compile, as the following snippet demonstrates:

package oreilly.hcj.nested;
public class OuterClass {
   private static class SomeOtherClass {
      // ...code
   }
}

package oreilly.hcj.nested;
public class StaticNestedClassDemo {
   public static void main(final String[] args) {
      OuterClass.SomeOtherClass prot =
         new OuterClass.SomeOtherClass( ); // <= Compiler error
      OuterClass.doPrivate( );
   }
}

Private and protected static nested classes have predictable visibility and specialized functions. Generally,they are used to implement internal features of a class or class hierarchy. For example,the Java collection classes use private static nested classes to implement the entries in their data structures. (For an example,see the source code to java.util.HashTable.) On the other hand,public static nested classes are often used to group classes together or give a group of classes access to private static resources.

When using static nested classes,your first concern should be whether package scope is sufficient for your class. If it isn't,then by all means use static nested classes. If package scope will suffice,then it is better,for the sake of readability,to declare the class normally.

Of all the nested classes,static nested classes are the easiest to use,read,and understand. Unless you positively need to access the instance variables of a class,I recommend you make your nested classes static whenever possible.

Double Nested Classes

One peculiar form of declaration that I came across while working for an aerospace company was a nested class within a nested class. The dynamics of this sort of declaration are similar to those of a normal nested class. The difference is that the dynamics extend to the subnested class as well as the nested class. See Example 6-9.

Example 6-9. A double nested class

package oreilly.hcj.nested;
public class DoubleNestedClass {
   private static final String name = "Robert";
   private static String company = "O'Reilly";

   private int value = 5;

   public static String getCompany( ) {
      return company;
   }

   public static String getName( ) {
      return name;
   }

   public int getValue( ) {
      return value;
   }

   public static class SomeClass {
      private final static String book = "Hardcore Java";

      public void someMethod( ) {
         System.out.println("In SomeClass.someMethod( )");
         System.out.println(company);
         System.out.println(name);
         // System.out.println(value); // <= Compiler error
      }

      public static class SomeOtherClass {
         public void someMethod( ) {
            System.out.println("In SomeOtherClass.someMethod( )");
            System.out.println(company);
            System.out.println(book);
         }
      }
   }

}

In this example,the class SomeOtherClass is nested inside of the class SomeClass. Because of the double nesting, SomeOtherClass can access the private final static members of DoubleNestedClass as well as those of SomeClass. The dynamics extend down the nesting. Similar rules apply to method-scoped and instance-scoped double nested classes. For example, a method-scoped double nested class would have access to instance variables and final local variables of the declaring method. Regardless of how deep the nesting is, the rules propagate down.

However, just because you can use double nested classes doesn't necessarily mean you should. In fact, I can't think of a single legitimate reason for double nested classes. If you find yourself writing one of these, you should probably rethink your architecture. If you find yourself doing this routinely, you may want to consider taking a long vacation.

Nested Classes in Interfaces?

Java supports the concept of nested classes in interfaces. The syntax and dynamics work just like nested classes declared in a class. However, declaring a class nested inside an interface would be extremely bad programming. An interface is an abstraction of a concept, not an implementation of one. Therefore, implementation details should be left out of interfaces. Remember, just because you can cut off your hand with a saw doesn't mean that it's a particularly good idea.

Nested Interfaces

As with classes, you can nest interfaces inside an outer class or another interface. The dynamics are similar to nested classes. However, keep in mind that the classes that ultimately implement these nested interfaces don't have the special access to the class members that a nested class does; instead,they behave just like normally declared classes.

Nested Interfaces in Classes

The syntax for nested interfaces is the same as nested classes, but the results are quite different. Since you are defining only the interface and not the implementation, the user of the interface is free to implement the class any way he wants. A good example of a nested interface implementation is with a special collection. See Example 6-10.

Example 6-10. A nested interface

package oreilly.hcj.nested;
public class Inventory {
   public HashSet items;

   public Set getValues( ) {
      return Collections.unmodifiableSet(items);
   }

   public void addItem(final InventoryItem item) {
      items.add(item);
   }

   public Iterator iterator( ) {
      return items.iterator( );
   }

   public void removeElement(final InventoryItem item) {
      items.remove(item);
   }

   public static interface InventoryItem {
      public String getSKU( );
   }
}

In this example, the special collection named Inventory will contain only objects that implement the InventoryItem interface (and therefore define the method getSKU( )). Furthermore, since you don't want to tie down the inventory items to a particular superclass, you make an interface for the inventory items, which allows the user to implement that interface as he sees fit.

There are only a few good reasons why you should use inner interfaces. One is to model a composition relationship without restricting the implementation of the composed object. In this case, you would need an inner interface to model the compositional relationship and to let the user of the interface decide on the implementation.

Nested interfaces are useful on rare occasions, but most of the time you can achieve the same results simply by declaring the interface in its own file.

Nested Interfaces in Interfaces

It is also possible to nest interfaces within other interfaces, although this is rarely done. The syntax is the same as with nested classes. Although using interfaces nested in other interfaces is less crazy than nesting classes within an interface, it is still pretty weird.

Essentially, when you nest an interface inside another interface, you are describing a compositional relationship among interfaces. Conceptually, it is a rather strange thing to do. Therefore, I don't recommend nesting interfaces.

Ultimately, nesting classes or interfaces inside other interfaces should probably be blocked by the compiler because using them makes no sense in object-oriented programming. All you can hope to accomplish is to demonstrate some obscure aspects of Java for the admiration of your junior developers. Its practical usefulness is about zero.

Nested Class Rules

Nested classes have many confusing rules. Table 6-1 will help you keep them straight.

Table 6-1. Nested class references

TypeScopeAccessKeywords
 methodinstanceclassmethod finalclass finalclass staticinstanceabstract/finalprivateprotectedpublic
Anonymous*  ****    
Limited-scope*  *****   
Inner *  *******
Static nested  * ** ****

Although most of the nested classes in this chapter are not recommended for routine use, it is important that you understand their dynamics. You may come across these nested class types down the line,and knowing how they work could save you time and frustration.

Although many developers tend to overuse nested classes, they can be a valuable asset to your development process. When it comes to modeling composition relationships and implementing internals of a class that you want to hide, they can't be beat.

Robert Simmons, Jr. lives and works as a senior software architect in Germany. He is the author of O'Reilly's Hardcore Java.


Return to ONJava.com.