简体   繁体   中英

Inner classes with the same name as an outer class?

Constraints:

I have a maven source code generator that I wrote that is creating POJO classes from some data files that have nested namespaces. I want each namespace to be nested as an inner class. In some cases out of my control I end up with inner classes that are the same simple name as the outermost class.

All the classes must be public scope as this is for a type safe wrapper over something like a properties file, but hierarchical..

I can't change the names otherwise I am changing the names meaning and the namespace that is enclosing data.

Given than I have the following code:

public class A
{
    public class B
    {
        public class A
        {

        }
    }
}

Inner classes should append the name of the outer class to form a unique namespace such as A$B$A.class , I haven't found a valid reason for this not to compile.

Is there any trick to get this to compile?

No. From the JLS section on class declarations:

It is a compile-time error if a class has the same simple name as any of its enclosing classes or interfaces.

Note: I somehow managed to miss this on my first pass through looking for an explicit rule. Check the edit history if you want the tortuous way I got here.

You asked: Is there any trick to get this to compile? .

The answer is: Well, maybe....

迷宫

Create a class like the following:

public class A
{
    public class B
    {
        public class X
        {
        }
    }
}

And a class where this class is going to be used

public class AUse
{
    public static void main(String[] args)
    {
        A.B.X aba = new A().new B().new X();
        System.out.println("Created "+aba+" of class "+aba.getClass());
    }
}

Then, download the Apache Byte Code Engineering Library (BCEL) , and create and run the following class:

import java.io.FileOutputStream;

import org.apache.bcel.Repository;
import org.apache.bcel.util.BCELifier;

public class CreateCreators
{
    public static void main(String[] args) throws Exception
    {
        new BCELifier(
            Repository.lookupClass("A"),
            new FileOutputStream("ACreator.java")).start();
        new BCELifier(
            Repository.lookupClass("A$B"),
            new FileOutputStream("A$BCreator.java")).start();
        new BCELifier(
            Repository.lookupClass("A$B$X"),
            new FileOutputStream("A$B$XCreator.java")).start();
        new BCELifier(
            Repository.lookupClass("AUse"),
            new FileOutputStream("AUseCreator.java")).start();
    }
}

This uses the BCELifier class from the BCEL. This is a class that takes a .class file, and creates a .java file that can be compiled to a .class file, that, when it is executed, creates the .class file that it was originally fed with. (Side note: I love this library).

So the A$B$XCreator.java file that is created there contains the BCEL code that is necessary to create the A$B$X.class file. This consists of statements like the generation of the constant pool and the instructions:

...
_cg = new ClassGen("A$B$X", "java.lang.Object", "A.java", 
    ACC_PUBLIC | ACC_SUPER, new String[] {  });
...
il.append(_factory.createFieldAccess("A$B$X", "this$1", 
    new ObjectType("A$B"), Constants.PUTFIELD));

Similarly, the AUseCreator.java contains the BCEL code that creates the AUse.class . For example, the instruction of the constructor invocation of `A$B$X':

...
il.append(_factory.createInvoke("A$B$X", "<init>", Type.VOID, 
    new Type[] { new ObjectType("A$B") }, Constants.INVOKESPECIAL));

Now you can simply replace the String occurrences of "A$B$X" with "A$B$A" in the A$B$XCreator.java and AUseCreator.java , and then compile and run these classes.

The result will be a A$B$A.class file, and a AUse.class file that uses the A$B$A.class . Executing the AUse will print

Created A$B$A@15f5897 of class class A$B$A

I'm not sure whether this is considered as a "trick", or whether it still can be called "compiling" at all, but there is a way, at least. The key point is here, of course, that the fact that it did not compile is solely due to a limitation of the language , but there is no reason why this should not be representable in form of class files, regardless of how they are created.

You can't get it to compile, but more importantly, why would you need to ?

What's wrong with:

public class A
{
    public class B
    {
        public class InnerA
        {

        }
    }
}

This seems like a design problem that you need to fix. If you can't rename it, consider anonymous inner classes. Or take some of those classes outside. Or just don't even use them.

It's a bit of a hack, but this compiles at my machine:

class A
{
    public class B
    {
        public class Α
        {

        }
    }
}

Try it. Literally: copy-past this thing ;)

SPOILER:

The name of the inner class is a capital letter alpha of the Greek alphabet. It's a Unicode character.

Depending on what you're after, the following might work for you:

public class A {
  class B extends C {
  }

  public static void main(String[] args) {
    new A().new B().new A();
  }
}

class C {
  class A {
    {
      System.out.println(getClass());
    }
  }
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM