This discussion is archived
10 Replies Latest reply: Sep 18, 2008 10:51 AM by 807589 RSS

Unexpected class name for declared inner class of anonymous inner class

807589 Newbie
Currently Being Moderated
A numeric prefix is showing up in class name of a nested class declared inside of an inner class. Where is the '1' in "com.mycompany.Test$3$1Named" coming from? Is this proper behavior? If so, what's the point of this? Doesn't seem to happen further down the nesting tree; only for the first named class descending from an anonymous one.

package com.mycompany;

public class Test {
     private interface ClassStorage {Class<?> getClazz();}

     public static Class<?> ANONYMOUS_CLASS = (new Object() {}).getClass();

     public static  Class<?> ANONYMOUS_CHILD_OF_ANONYMOUS_CLASS = (
          new ClassStorage() {
               public Class<?> getClazz() {return (new Object(){}).getClass();}
          }
     ).getClazz();

     public static Class<?> NAMED_CHILD_OF_ANONYMOUS_CLASS = (
          new ClassStorage() {
               public Class<?> getClazz() {
                    class Named {}
                    return Named.class;
               }
          }
     ).getClazz();

     public static Class<?> NAMED_CHILD_OF_NAMED_CHILD_OF_ANONYMOUS_CLASS = (
          new ClassStorage() {
               public Class<?> getClazz() {
                    class Named {class InnerNamed {}}
                    return Named.InnerNamed.class;
               }
          }
     ).getClazz();

     public static Class<?> ANONYMOUS_CHILD_OF_NAMED_CHILD_OF_ANONYMOUS_CLASS = (
          new ClassStorage() {
               public Class<?> getClazz() {
                    class Named implements ClassStorage {
                         public Class<?> getClazz() {return (new Object() {}).getClass();}
                    }
                    return (new Named()).getClazz();
               }
          }
     ).getClazz();

     public static Class<?> NAMED_CHILD_OF_ANONYMOUS_CHILD_OF_NAMED_CHILD_OF_ANONYMOUS_CLASS =
      (
          new ClassStorage() {
               public Class<?> getClazz() {
                    class Named implements ClassStorage {
                         public Class<?> getClazz() {
                              return (
                                   new ClassStorage() {
                                        class InnerNamed {}
                                        public Class<?> getClazz() {return InnerNamed.class;}
                                   }
                              ).getClazz();
                         }
                    }
                    return (new Named()).getClazz();
               }
          }
      ).getClazz();

     public static void main(String[] pArgs) {
          System.out.println(ClassStorage.class.getName());
          System.out.println(ANONYMOUS_CLASS.getName());
          System.out.println(ANONYMOUS_CHILD_OF_ANONYMOUS_CLASS.getName());
          System.out.println(NAMED_CHILD_OF_ANONYMOUS_CLASS.getName());
          System.out.println(NAMED_CHILD_OF_NAMED_CHILD_OF_ANONYMOUS_CLASS.getName());
          System.out.println(ANONYMOUS_CHILD_OF_NAMED_CHILD_OF_ANONYMOUS_CLASS.getName());
          System.out.println(NAMED_CHILD_OF_ANONYMOUS_CHILD_OF_NAMED_CHILD_OF_ANONYMOUS_CLASS.getName());
     }
}
  • 1. Re: Unexpected class name for declared inner class of anonymous inner class
    807589 Newbie
    Currently Being Moderated
    Actually a numeric suffix is added the name of inner/nested/method local classes.
    In Java you don't have to give a class a name, but at the JVM level each class must be in its own file and have a unique name.
    So the compiler generates the names for you.
  • 2. Re: Unexpected class name for declared inner class of anonymous inner class
    807589 Newbie
    Currently Being Moderated
    I'm aware that anonymous inner classes are named automatically. That doesn't fully explain the output:

    com.mycompany.Test$ClassStorage
    com.mycompany.Test$1
    com.mycompany.Test$2$1
    com.mycompany.Test$3$1Named
    com.mycompany.Test$4$1Named$InnerNamed
    com.mycompany.Test$5$1Named$1
    com.mycompany.Test$6$1Named$1$InnerNamed

    I expected each qualified classname to be fully numeric or to match [a-zA-Z]\w*. "1Named" seems inconsistent. The '1' is not necessary for uniqueness.
  • 3. Re: Unexpected class name for declared inner class of anonymous inner class
    807589 Newbie
    Currently Being Moderated
    hwaite wrote:
    I'm aware that anonymous inner classes are named automatically. That doesn't fully explain the output:

    com.mycompany.Test$ClassStorage
    com.mycompany.Test$1
    com.mycompany.Test$2$1
    com.mycompany.Test$3$1Named
    com.mycompany.Test$4$1Named$InnerNamed
    com.mycompany.Test$5$1Named$1
    com.mycompany.Test$6$1Named$1$InnerNamed

    I expected each qualified classname to be fully numeric or to match [a-zA-Z]\w*. "1Named" seems inconsistent. The '1' is not necessary for uniqueness.
    It is not "1Named", it is "$1" and "Named".
  • 4. Re: Unexpected class name for declared inner class of anonymous inner class
    807589 Newbie
    Currently Being Moderated
    It's not really a name of the class, it's the name of the resource that represents the class, so your runtime can locate it. The naming scheme is for the convenience of your tools, not for you to use. AFAIK, this behaviour isn't defined by the JLS, it's implementation specific. In short, don't worry about it
  • 5. Re: Unexpected class name for declared inner class of anonymous inner class
    807589 Newbie
    Currently Being Moderated
    Interesting. Perhaps I'm going about things the wrong way. I'm writing a checkstyle check that traverses an ANTLR-spawned syntax tree. I've got a stack of nested class identifiers and need to resolve it to a Class object. In the case of 'com.mycompany.Test$3$1Named', my Stack contains ["Test", "3", "Named"]. Is there no implementation-independent way for me to generate an argument to Class.forName(String)? Perhaps this is part of the reason that none of the existing checks seem to use reflection...
  • 6. Re: Unexpected class name for declared inner class of anonymous inner class
    807589 Newbie
    Currently Being Moderated
    The FQN of an inner class is simply the FQN of the enclosing type, with the inner type's simple name on the end. Eg
    package com.blah.foo.etc;
    
    class Outer {
    
      class Inner {} 
    
    }
    The FQN of Inner is com.blah.foo.etc.Outer.Inner

    Of course, by definition, anonymous inner classes don't have names as such.
  • 7. Re: Unexpected class name for declared inner class of anonymous inner class
    807589 Newbie
    Currently Being Moderated
    I've found that
    Class.forName("com.mycompany.Test$3$1Named")
    works fine while
    Class.forName("com.mycompany.Test$3$Named")
    results in an exception. My AST traversal code keeps track of how many anonymous classes it's passed so I've been able to introspect on unnamed inner classes no problem. I just don't fully understand the algorithm being used to create identifiers for named inner classes of anonymous outer classes. Well, I've managed to make things work by accounting for the mysterious '1'. Sounds like the code won't be JVM independent, though. C'est la vie. Thx for the input, all.
  • 8. Re: Unexpected class name for declared inner class of anonymous inner class
    807589 Newbie
    Currently Being Moderated
    hwaite wrote:
    I've found that
    Class.forName("com.mycompany.Test$3$1Named")
    works fine while
    Class.forName("com.mycompany.Test$3$Named")
    results in an exception. My AST traversal code keeps track of how many anonymous classes it's passed so I've been able to introspect on unnamed inner classes no problem. I just don't fully understand the algorithm being used to create identifiers for named inner classes of anonymous outer classes. Well, I've managed to make things work by accounting for the mysterious '1'. Sounds like the code won't be JVM independent, though. C'est la vie. Thx for the input, all.
    To be honest, I'd wonder why you even needed to get hold of them
  • 9. sadistic input
    807589 Newbie
    Currently Being Moderated
    georgemc wrote:
    To be honest, I'd wonder why you even needed to get hold of them
    Checkstlye checks traverse an abstract syntax tree in search of code smells. Checks should be capable of operating against arbitrarily complex source files. All of the existing checks appear to identify problematic idioms based purely upon the tree data. I'm writing some more advanced checks that also rely upon upon information about the class itself: what class it extends, which interfaces it implements, etc. This means that, for a given node in the AST, I need to get at the relevant Class object. In the end, anyone crazy enough to nest classes in this fashion probably gets what they deserve.
  • 10. Re: sadistic input
    807589 Newbie
    Currently Being Moderated
    hwaite wrote:
    georgemc wrote:
    To be honest, I'd wonder why you even needed to get hold of them
    Checkstlye checks traverse an abstract syntax tree in search of code smells. Checks should be capable of operating against arbitrarily complex source files
    Source files. Previously, you were talking about class files. Compilers can butcher code in all manner of ways that make the compiled result look somewhat different to the source code, and, more importantly from your point-of-view, make them smellier. I'd forget about this approach, and work on source code itself. Eclipse JDT has some good tools for parsing and working with source code, you could start with that rather than from scratch