A static member class or a static member interface comprises the same declarations as those allowed in an ordinary top-level class or interface. A static member class must be declared as a static member of an enclosing class or interface. Nested interfaces are considered implicitly static, the keyword static can, therefore, be omitted. Since static member classes and interfaces are members of an enclosing class or interface, they can have any member accessibility.
Static member classes and interfaces can only be nested within other static member or top-level classes and interfaces.
In Example 7.2, the top-level class TopLevelClass at (1) contains a static member class StaticMemberClass_1 at (2), which in turn defines a static member interface StaticMemberInterface_1_1 at (3) and a static member class StaticMemberClass_1_1 at (4). The static member class StaticMemberClass_1_1 at (4) implements the static member interface StaticMemberInterface_1 at (5). Note that each static member class is defined as static, just like static variables and methods in a top-level class.
// Filename: TopLevelClass.java package express; public class TopLevelClass { // (1) // ... public static class StaticMemberClass_1 { // (2) // ... private interface StaticMemberInterface_1_1 { // (3) // ... } public static class StaticMemberClass_1_1 implements StaticMemberInterface_1 { // (4) // ... } } interface StaticMemberInterface_1 extends StaticMemberClass_1.StaticMemberInterface_1_1 { // (5) // ... } } class AnotherTopLevelClass implements TopLevelClass.StaticMemberInterface_1 { // (6) TopLevelClass.StaticMemberClass_1.StaticMemberClass_1_1 objRef1 = new TopLevelClass.StaticMemberClass_1.StaticMemberClass_1_1(); // (7) //TopLevelClass.StaticMemberClass_1.StaticMemberInterface_1_1 ref; // (8) // ... }
The full name of a (static or non-static) member class or interface includes the names of the classes and interfaces it is lexically nested in. For example, the full name of the member class StaticMemberClass_1_1 at (4) is TopLevelClass.StaticMemberClass_1.StaticMemberClass_1_1. The full name of the member interface StaticMemberInterface_1 at (5) is TopLevelClass.StaticMemberInterface_1. Each member class or interface is uniquely identified by this naming syntax, which is a generalization of the naming scheme for packages. The full name can be used in exactly the same way as any other top-level class or interface name, as shown at (6) and (7). Such a member's fully qualified name is its full name prefixed by the name of its package. For example, the fully qualified name of the member class at (4) is express.TopLevelClass.StaticMemberClass_1.StaticMemberClass_1_1. Note that a member class or interface, cannot have the same name as an enclosing class, interface, or package.
For all intents and purposes, a static member class or interface is very much like any other top-level class or interface. Static variables and methods belong to a class, and not to instances of the class. The same is true for static member classes and interfaces.
Within the scope of its top-level class or interface, a member class or interface can be referenced regardless of its accessibility modifier and lexical nesting, as shown at (5) in Example 7.2. Its accessibility modifier (and that of the types making up its full name) come into play when it is referenced in an external client. The declaration at (8) in Example 7.2 will not compile because the member interface TopLevelClass.StaticMemberClass_1.StaticMemberInterface_1_1 has private accessibility.
A static member class can be instantiated without any reference to any instance of the enclosing context, as is the case for instantiating top-level classes. An example of creating an instance of a static member class is shown at (7) in Example 7.2, using the new operator.
If the file TopLevelClass.java containing the declarations in Example 7.2 is compiled, it will result in the generation of the following class files, where each file corresponds to a class or interface definition:
TopLevelClass$StaticMemberClass_1$StaticMemberClass_1_1.class TopLevelClass$StaticMemberClass_1$StaticMemberInterface_1_1.class TopLevelClass$StaticMemberClass_1.class TopLevelClass$StaticMemberInterface_1.class TopLevelClass.class AnotherTopLevelClass.class
Note how the full class name corresponds to the class file name (minus the extension), with the dollar sign ($) replaced by the dot sign (.).
There is seldom any reason to import member classes or interfaces from packages. It would undermine the encapsulation achieved by such classes or interfaces. However, a compilation unit for a named package can use the import declaration to provide a short cut for the names of member classes and interfaces. (Java does not allow imports from the unnamed package.) Some variations on usage of the import declaration for member classes are shown in Example 7.3.
// Filename: Client1.java package express; import express.TopLevelClass.*; // (1) public class Client1 { StaticMemberClass_1.StaticMemberClass_1_1 objRef1 = new StaticMemberClass_1.StaticMemberClass_1_1(); // (2) } ________________________________________________________________________ // Filename: Client2.java package express; import express.TopLevelClass.StaticMemberClass_1.*; // (3) public class Client2 { StaticMemberClass_1_1 objRef2 = new StaticMemberClass_1_1(); // (4) } class SomeClass implements express.TopLevelClass.StaticMemberInterface_1 { // (5) /* ... */ }
In the file Client1.java, the import statement at (1) allows the static member class StaticMemberClass_1_1 to be referenced as StaticMemberClass_1.StaticMemberClass_1_1 as at (2), whereas in the file Client2.java, the import statement at (3) will allow the same class to be referenced using its simple name, as at (4). At (5) the fully qualified name of the static member interface is used in an implements clause.
Static code does not have a this reference and can, therefore, only directly access other members that are declared static within the same class. Since static member classes are static, they do not have any notion of an enclosing instance of the enclosing context. This means that any code in a static member class can only directly access static members in its enclosing context, not instance members in its enclosing context.
Figure 7.1 is a class diagram that illustrates static member classes and interfaces. These are shown as members of the enclosing context, with the {static} tag to indicate that they are static, that is, they can be instantiated without regard to any object of the enclosing context. Since they are members of a class or an interface, their accessibility can be specified exactly like that of any other member of a class or interface. The classes from the diagram are implemented in Example 7.4.
// Filename: AccessInTopLevelClass.java public class AccessInTopLevelClass { // (1) public void nonStaticMethod_1() { // (2) System.out.println("nonstaticMethod_1 in AccessInTopLevelClass"); } private static class StaticMemberClass_1 { // (3) private static int i; // (4) private int j; // (5) public static void staticMethod_1_1() { // (6) System.out.println("staticMethod_1_1 in StaticMemberClass_1"); } interface StaticMemberInterface_1_1 { int Y2K = 2000; } // (7) protected static class StaticMemberClass_1_1 implements StaticMemberInterface_1_1 { // (8) private int k = Y2K; // (9) public void nonStaticMethod_1_1_1() { // (10) // int jj = j; // (11) Not OK. int ii = i; // (12) int kk = k; // (13) // nonStaticMethod_1(); // (14) Not OK. staticMethod_1_1(); // (15) } public static void main (String[] args) { int ii = i; // (16) // int kk = k; // (17) Not OK. staticMethod_1_1(); // (18) } } } }
Output from the program:
staticMethod_1_1 in StaticMemberClass_1
Example 7.4 demonstrates accessing members directly in the enclosing context of class StaticMemberClass_1_1 defined at (8). The initialization of the field k at (9) is valid, since the field Y2K, defined in the outer interface StaticMemberInterface_1_1 at (7), is implicitly static. The compiler will flag an error at (11) and (14) in the method nonStaticMethod_1_1_1(), because direct access to non-static members in the enclosing class is not permitted by any method in a static member class. It will also flag an error at (17) in the method main(), because a static method cannot access directly other non-static fields in its own class. Statements at (16) and (18) access static members only in the enclosing context. The references in these statements can also be specified using full names.
int ii = AccessInTopLevelClass.StaticMemberClass_1.i; AccessInTopLevelClass.StaticMemberClass_1.staticMethod_1_1();
Note that a static member class can define both static and instance members, like any other top-level class. However, its code can only directly access static members in its enclosing context.
A static member class, being a member of the enclosing class or interface, can have any accessibility (public, protected, package/default, private), like any other member of a class. The class StaticMemberClass_1 at (3) has private accessibility, whereas its nested class StaticMemberClass_1_1 at (8) has protected accessibility. The class StaticMemberClass_1_1 defines the method main(), which can be executed by the command
>java AccessInTopLevelClass$StaticMemberClass_1$StaticMemberClass_1_1
Note that the class StaticMemberClass_1_1 is specified using the full name of the class file, minus the extension.