user177800
user177800

Reputation:

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?

Upvotes: 13

Views: 4545

Answers (5)

Marco13
Marco13

Reputation: 54639

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

The answer is: Well, maybe....

Maze

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.

Upvotes: 9

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.

Upvotes: 12

Oleksandr Kelepko
Oleksandr Kelepko

Reputation: 234

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());
    }
  }
}

Upvotes: 0

Martijn Courteaux
Martijn Courteaux

Reputation: 68857

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.

Upvotes: 2

Anubian Noob
Anubian Noob

Reputation: 13596

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.

Upvotes: 2

Related Questions