Reputation:
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.
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
Reputation: 54639
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.
Upvotes: 9
Reputation: 77187
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
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
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
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