Joseph D. Darcy's Sun Weblog

Joseph D. Darcy's Sun Weblog


20070605 Tuesday June 05, 2007

Nested, Inner, Member, and Top-Level Classes

One way declared types in Java differ from one another is whether the type is a class (which includes enums) or an interface (which includes annotation types). An independent property of a type is its relation to the surrounding lexical context. A top level class does not appear inside another class or interface. If a type is not top level it is nested. However, there are a number of distinct ways a type can be nested. First, a type can be a member of another type; a member type is directly enclosed by another type declaration. All member types have names; however, some member types are inner classes and others are not. If a type is explicitly or implicitly static, it is not an inner class; all member interfaces are implicitly static.

Inner classes also include local classes, which are named classes declared inside of a block like a method or constructor body, and anonymous classes, which are unnamed classes whose instances are created in expressions and statements. Anonymous classes are used to implement specialized enum constants. Inner classes have access to instance variables of any lexically enclosing instance; that is, the fields of the object an inner class is created in reference to. However, not all inner classes have enclosing instances; inner classes in static contexts, like an anonymous class used in a static initializer block, do not.

The Venn diagram below shows how these distinctions relate and combine; in particular, member-ness and inner-ness are not orthogonal properties.

Venn Diagram of Class Nesting Kinds

pdf of diagram

A reflective API providing a complete model of the language needs to allow these differences to be determined. Two reflective APIs providing this information are core reflection as of JDK 5 and javax.lang.model from JSR 269 in JDK 6; however, each API exposes the data differently. (The legacy apt mirror API finesses the issue by not modeling local and anonymous classes.)

Core reflection uses java.lang.Class to model types. When inner classes were introduced back in JDK 1.1, a getDeclaringClass method was added to Class. While this supports member types, relevant information about local and anonymous classes was not directly available. To remedy this, JDK 5 added a number of methods to return the enclosing entity, if any, and identify what kind of nesting a type may have:

Because of the lack of an usable supertype, the different kinds of enclosing elements (classes, methods, and constructors) must be retrieved from different methods. If the class is not so enclosed, a null is returned. Therefore the code to find the enclosing element is a sequence of if-methodA-not-equal-null-else-if-methodB-not-equal-null tests.

Starting with a clean slate, javax.lang.model was able to provide a cleaner way of modeling these distinctions. First, the functionality of getDeclaringClass and the three getEnclosingFoo methods is provided by the single getEnclosingElement method, which returns the immediately lexically enclosing element regardless of whether it is a class, or method, or constructor. Second, for types the getNestingKind method returns one of the NestingKind enum constants, ANONYMOUS, LOCAL, MEMBER, or TOP_LEVEL. Those constants clearly correspond to the possible alternatives displayed in the diagram above. While it would be technically possible to add a getNestingKind method to Class, that would create an undesired dependency of a java.lang.* class on a javax.* package.

(2007-06-05 17:11:35.0) Permalink Comments [7]

Calendar

« June 2007 »
SunMonTueWedThuFriSat
     
1
2
3
4
6
7
8
9
10
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
       
Today

RSS Feeds

XML
All
/Annotation Processing
/General
/Java
/JavaOne
/Numerics
/OpenJDK

Search

Links

    Blogroll
  • Download the JRE

    News

Navigation



Referers

Today's Page Hits: 1104