与外部类同名的内部类?

13

限制条件:

我编写了一个Maven源代码生成器,它从一些具有嵌套命名空间的数据文件中创建POJO类。我希望每个命名空间都作为内部类嵌套。在某些无法控制的情况下,我会得到与最外层类相同简单名称的内部类。

所有类必须是public范围,因为这是针对类似于属性文件但分层的类型安全包装器。

否则我将改变名称的含义和封闭数据的命名空间。

鉴于我拥有以下代码:

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

        }
    }
}

内部类应该在其名称后追加外部类的名称以形成唯一的名称空间,例如A$B$A.class。我没有找到不编译的有效原因。

有什么技巧可以使其编译吗?


11
拜托了:为什么?就我所知,不是。 - Martijn Courteaux
5
对于那些回答“那就别那么做”的人:这是一个有趣的理论问题,“这不起作用”并没有特别好地回答这个问题。 - chrylis -cautiouslyoptimistic-
技巧有哪些限制?一个显而易见的技巧是“重命名”,但我怀疑你不想这样做。为了被认为是有效的技巧,结果应该具备哪些属性? - user2357112
5个回答

12

没有从JLS关于类声明的部分:

如果一个类的简单名称与其封闭类或接口的任何一个相同,则会在编译时出现错误。

注意:在第一次查找明确规则时,我不知道怎么错过了这个。如果您想要我到达这里的曲折方式,请查看编辑历史记录。


9
你问道:有没有什么技巧可以让这个编译通过? 答案是:也许有……

Maze

创建一个类如下所示:
public class A
{
    public class B
    {
        public class X
        {
        }
    }
}

还有一个使用这个类的类

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

然后,下载Apache字节码工程库(BCEL),并创建并运行以下类:
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();
    }
}

这里使用了BCEL中的BCELifier类。这是一个类,它接受一个.class文件,并创建一个.java文件,可以编译成一个.class文件。当它被执行时,它会创建最初提供的.class文件。 (顺便说一句:我喜欢这个库)。

因此,创建的A$B$XCreator.java文件包含了创建A$B$X.class文件所必需的BCEL代码。这包括生成常量池和指令的语句:

...
_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));

同样地,AUseCreator.java包含创建AUse.class的BCEL代码。例如,在`A$B$X'的构造函数调用指令中:
...
il.append(_factory.createInvoke("A$B$X", "<init>", Type.VOID, 
    new Type[] { new ObjectType("A$B") }, Constants.INVOKESPECIAL));

现在,您只需要在 A$B$XCreator.javaAUseCreator.java 中简单地将字符串出现的 "A$B$X" 替换为 "A$B$A" ,然后编译并运行这些类。
结果将是一个 A$B$A.class 文件和一个使用 A$B$A.classAUse.class 文件。执行 AUse 将会打印:
Created A$B$A@15f5897 of class class A$B$A

我不确定这是否被认为是一种“技巧”,或者它是否仍然可以被称为“编译”,但至少有一种方法。当然,关键点在于它无法编译仅仅是由于语言的限制,但无论如何,这应该可以以class文件的形式表示,而不管它们是如何创建的。


2

你无法编译它,但更重要的是,为什么需要编译

以下方式有何问题:

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

        }
    }
}

看起来这似乎是一个需要修复的设计问题。如果无法重命名,请考虑使用匿名内部类,或将其中一些类移到外面去,或者干脆不使用它们。


2

这是一个有点繁琐的方法,但可以在我的机器上编译:

class A
{
    public class B
    {
        public class Α
        {

        }
    }
}

试一下吧。 字面意思:复制粘贴这个东西 ;)

SPOILER:

内部类的名称是希腊字母大写字母α。它是一个Unicode字符。


那如果外部的A需要是公共的呢?我的意思是,问题中有一个公共的外部A... - Anubian Noob
是的,我知道,但我是在另一个Java文件中测试这个,而且你只能有一个公共类。那不是hack :) 如果你想的话,可以将其设为public :) - Martijn Courteaux
这段代码在我的机器上编译不了。似乎不支持Unicode... 还有,那是个正常的A。 - Anubian Noob
2
我赞赏你的创造力,但诅咒任何真正使用这种东西于实际代码的人。 - Reinstate Monica
可悲的是,这只是一种花哨的重命名方式。 - Artjom B.

0

根据你的需求,以下内容可能适合你:

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

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接