Overview of Nested Type Declarations – Nested Type Declarations

9.1 Overview of Nested Type Declarations

A type declaration allows a new reference type to be defined. A type declaration can either be a top-level type declaration or a nested type declaration. Figure 9.1 gives an overview of the different kinds of type declarations that can be defined in Java.

Figure 9.1 Overview of Type Declarations

A top-level type declaration is a type declaration that is not defined inside another type declaration. A top-level type declaration can be any one of the following: a top-level class, a top-level interface, a top-level enum type, or a top-level record class. We invariably use the shorter names class, interface, enum type, and record class when it is clear we are not referring to their nested counterparts.

A nested type declaration is a type declaration that is defined inside another declaration. There are three categories of nested types:

  • Static member types (a.k.a. static nested types)
  • Inner classes (a.k.a. non-static nested classes)
  • Static local types (a.k.a. static local nested types)

As the name implies, static member types can be declared as a static member of either a top-level type or a nested type declaration. There are four kinds of static member types:

  • Static member class (a.k.a. static nested class)
  • Static member interface (a.k.a. nested interface)
  • Static member enum type (a.k.a. nested enum type)
  • Static member record class (a.k.a. nested record class)

A static member class or record class can be instantiated like any ordinary top-level class or record class, using its qualified name when calling the constructor with the new operator. It is not possible to instantiate an enum type or an interface.

Inner classes are non-static nested classes. There are three kinds of inner classes:

  • Non-static member class
  • Local class
  • Anonymous class

Inner classes differ from static member classes in one important aspect: An instance of an inner class has an instance of the enclosing class (called the immediately enclosing instance) associated with it when the inner class is declared in a non-static context. An instance of an inner class can access the members of its enclosing instance by their simple names.

Non-static member classes are inner classes that are defined as instance members of other type declarations, just as fields and instance methods are defined in a class.

Local (normal) classes are non-static inner classes that can be defined in a block—a local block, a method body, an initializer block—both in static and non-static context, just as local variables can be defined in a block.

Static local types are defined in a block, but are implicitly static as opposed to local classes that are non-static. A static local type can be defined both in static and non-static context, just as local classes can be defined in a block. There are three kinds of static local types:

  • Static local interface
  • Static local enum type
  • Static local record class

Anonymous classes are inner classes that can be defined as expressions and in expression statements, both in static and non-static context, and instantiated on the fly.

In Figure 9.1 we see that there are four kinds of nested classes (static member classes, non-static member classes, local classes, anonymous classes), two kinds of nested interfaces (static member interfaces, static local interfaces), two kinds of nested enum types (static member enum types, static local enum types), and two kinds of nested record classes (static member record classes, static local record classes)—all defined by the context in which these nested types are declared.

Given the terminology introduced for nested types in Figure 9.1, a member type declaration can be any one of the following nested types: a static member type (class, interface, enum type, record class) or a non-static member class. Note that local classes, anonymous classes, local interfaces, local enum types, and local record classes are not member type declarations, as they cannot be declared as a member of a type declaration.

Example 9.1 Overview of Type Declarations

Click here to view code image

class TLC {                       // (1) Top-level class
  // Static member types:
  static class     SMC {}         // (2) Static member class
         interface SMI {}         // (3) Static member interface
         enum      SME {}         // (4) Static member enum
         record    SMR() {}       // (5) Static member record
  // Non-static member class:
  class NSMC {}                   // (6) Inner class
  // Local types in non-static context (analogous for static context):
  void nsm() {                    // Non-static method
    class     LC {}               // (7) Local class (inner class)
    interface SLI {}              // (8) Static local interface
    enum      SLE {}              // (9) Static local enum
    record    SLR() {}            // (10) Static local record
  }
  // Anonymous classes (here defined as initializer expressions):
         SMC nsf = new SMC() {};  // (11) Inner class in non-static context
  static SMI  sf = new SMI() {};  // (12) Inner class in static context
}

Skeletal code for nested types is shown in Example 9.1. Table 9.1 presents a summary of various aspects relating to nested types. Subsequent sections on each nested type elaborate on the summary presented in this table. (N/A in the table means “not applicable”.)

  • The Type column lists the different kinds of types that can be declared.
  • The Declaration context column lists the lexical context in which a type can be declared.
  • The Access modifiers column indicates what access can be specified for the type.
  • The Enclosing instance column specifies whether an enclosing instance is associated with an instance of the type.
  • The Direct access to enclosing context column lists what is directly accessible in the enclosing context from within the type.

Generic nested classes and interfaces are discussed in §11.13, p. 633. It is not possible to declare a generic enum type (§11.13, p. 635).

Locks on nested classes are discussed in §22.4, p. 1391.

Nested types can be regarded as a form of encapsulation, enforcing relationships between types by greater proximity. They allow structuring of types and a special binding relationship between a nested object and its enclosing instance. Used judiciously, they can be beneficial, but unrestrained use of nested types can easily result in unreadable code.

A word about the examples in this chapter: They are concocted to illustrate various aspects of nested types, and are not solutions to any well-defined or meaningful problems.

Table 9.1 Various Aspects of Type Declarations

TypeDeclaration contextAccess modifiersEnclosing instanceDirect access to enclosing context
Top-level class, interface, enum type, or record class (Top-level types)Packagepublic or package accessNoN/A
Static member class, interface, enum type, or record class (Static member types)As static member of a top-level type or a nested typeAll, except when declared in interfaces whose member type declarations are implicitly publicNoStatic members in enclosing context
Non-static member class (Inner class)As non-static member of a top-level type or a nested typeAllYesAll members in enclosing context
Local class (Inner class)In block with non-static contextNoneYesAll members in enclosing context plus final or effectively final local variables
In block with static contextNoneNoStatic members in enclosing context plus final or effectively final local variables
Anonymous class (Inner class)As expression in non-static contextNoneYesAll members in enclosing context plus final or effectively final local variables
As expression in static contextNoneNoStatic members in enclosing context plus final or effectively final local variables
Local interface, enum type, or record class (Static local types)In block with static and non-static contextNoneNoStatic members in enclosing context

Leave a Reply

Your email address will not be published. Required fields are marked *