Tuesday June 05, 2007
Joseph D. Darcy's Sun WeblogJoseph D. Darcy's Sun Weblog 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.
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
Core reflection uses
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
Starting with a clean slate, Post a Comment: Comments are closed for this entry. |
Calendar
RSS Feeds
All /Annotation Processing /General /Java /JavaOne /Numerics /OpenJDK SearchLinks
NavigationReferersToday's Page Hits: 833 |
|||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Posted by A. Sundararajan on June 05, 2007 at 09:44 PM PDT #
class Example { class ExampleFirst {} Object dollarFirst = new Object() {}; // declares instance of an anonymous class, the class's 'real' name is Example$1 }I am suggesting that Example$1 is a member class of Example, like ExampleFirst is.Posted by Howard Lovatt on June 06, 2007 at 06:22 PM PDT #
Howard,
Glad you found the explanation helpful. Much of the discussion of member types in the JLS deals with their names and how the names interact with other named entities. Since anonymous classes don't have names at the source level, they don't play in this space.
The binary names of member types have always been defined because they need a standard linkage convention to be able to be called from other classes. However, until JLSv3, the binary names of anonymous types have not been defined, even though Example$1 is traditional. Since anonymous classes can't be referred to directly from other source files, their linkage conventions are a compiler-internal contract and didn't have to be of the form Example$n. In part to help support the new reflection methods in JDK 5, the names of anonymous classes are now constrained to the traditional form, see JLSv3 section 13.1.
Posted by Joe Darcy on June 06, 2007 at 07:39 PM PDT #
I find that I am always having to make special cases for anonymous classes that then detect if the anonymous class is a local or a member and then call the handlers for local or member as appropriate. For the code I have written not having ANONYMOUS as a separate kind would simplify the code. This change is also consistent with getEnclosingElement that returns executable or type elements for the enclosing lexical scope.
Posted by Howard Lovatt on June 06, 2007 at 08:58 PM PDT #
Ah, I see. As a language model, when the JLS specifies an answer to a question, that is the answer we generally return. Nesting kind is not defined as a concept in the JLS and we had discussions over what to call this concept because of that omission. However, given that member, local, and anonymous classes are all defined in the JLS and we've chosen to present those distinctions with NestingKind, I believe anonymous classes must be identified as NestingKind.ANONYMOUS. The Java language can certainly be complicated in places and any complete model will share in that complexity. For example, anonymous classes can be used in static contexts, like a static initializer, where they don't have an enclosing instance.
Are you computing an "effective nesting-ness" before switching on the enum value?
Posted by Joe Darcy on June 06, 2007 at 10:03 PM PDT #
Paraphrasing the code that I write: I might have something like a switch on the kind and deal with local, member, and top level differently. When I get an anonymous class I then use getEnclosingElement and have something like a switch on the element type returned (e.g. via instanceof tests). Whilst this works it is annoying and I can't see an advantage in making anonymous a special case - its only difference is that it doesn't have a name.
With regard to your specific examples:
A class inside a static initializer I would treat as a local
The class of an enum instance I would treat as a member
I do agree that the JLS is not helpful on this point, in particular the distinction it makes between anonymous and other inner classes seems to serve no purpose. It just makes the explanation contorted.
Posted by Howard Lovatt on June 07, 2007 at 12:00 AM PDT #
A note on writing annotation processors, as mentioned in the documentation, it is not necessarily reliable to use instanceof tests to determine what sort of element an object represents. The API was designed so that the compiler's internal model and the JSR 269 model don't have to be the same; for example, all of the Element modeling interfaces can be implemented by the same class. A visitor or kind check should be used instead.
Posted by Joe Darcy on June 07, 2007 at 09:11 AM PDT #