Внутренние классы с тем же именем, что и внешний класс?

Ограничения:

У меня есть генератор исходного кода maven, который я написал, что создает классы POJO из некоторых файлов данных с вложенными пространствами имен. Я хочу, чтобы каждое пространство имен быть вложенным как внутренний класс. В некоторых случаях из-под контроля я в конечном итоге с внутренними классами, которые являются тем же самым простым именем, что и самые внешние класс.

Все классы должны быть областью public, поскольку это для безопасного типа обертка над чем-то вроде файла свойств, но иерархическая..

Я не могу изменить имена, иначе я изменяю значение имен и пространство имен который включает данные.

Учитывая, что у меня есть следующий код:

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

        }
    }
}

Внутренние классы должны добавлять имя внешнего класса для создания уникального пространства имен, такого как A$B$A.class, я не нашел правильной причины для этого не компилировать.

Есть ли какой-либо трюк, чтобы его скомпилировать?

Ответ 1

Нет. Из раздела JLS в объявлениях классов:

Это ошибка времени компиляции, если класс имеет то же простое имя, что и любой из его вмещающих классов или интерфейсов.

Примечание. Мне как-то удалось пропустить это на моем первом проходе, ища явное правило. Проверьте историю изменений, если вы хотите извилистый способ, который я получил здесь.

Ответ 2

Вы спросили: Есть ли какой-нибудь трюк, чтобы его скомпилировать?.

Ответ: Ну, может быть....

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 и создайте и запустите следующий класс:

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

Это использует класс BCELifier из BCEL. Это класс, который принимает файл .class и создает файл .java, который может быть скомпилирован в файл .class, который при его создании создает файл .class, с которым он был первоначально отправлен. (Сторона примечания: Мне нравится эта библиотека).

Итак, файл A$B$XCreator.java, который там создается, содержит код BCEL, необходимый для создания файла A$B$X.class. Это состоит из выражений, таких как формирование пула констант и инструкций:

...
_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 содержит код BCEL, который создает AUse.class. Например, инструкция вызова конструктора `A $B $X ':

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

Теперь вы можете просто заменить вхождения "A$B$X" String в "A$B$A" в A$B$XCreator.java и AUseCreator.java, а затем скомпилировать и запустить эти классы.

Результатом будет файл A$B$A.class и файл AUse.class, который использует A$B$A.class. Выполнение AUse приведет к печати

Created [email protected] of class class A$B$A

Я не уверен, считается ли это "трюком" или все еще можно назвать "компиляцией" вообще, но, по крайней мере, есть способ. Ключевым моментом здесь, конечно, является то, что тот факт, что он не компилировался, связан исключительно с ограничением языка, но нет причин, по которым это не должно быть представлено в виде файлов class, независимо от того, как они создаются.

Ответ 3

Вы не можете его скомпилировать, но что более важно, зачем вам нужно?

Что случилось с:

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

        }
    }
}

Это похоже на проблему с дизайном, которую нужно исправить. Если вы не можете переименовать его, рассмотрите анонимные внутренние классы. Или возьмите некоторые из этих классов за пределами. Или просто не используйте их.

Ответ 4

Это немного взломать, но это компилируется на моей машине:

class A
{
    public class B
    {
        public class Α
        {

        }
    }
}

Попробуйте. Буквально: скопируйте эту вещь;)

Спойлер:

Имя внутреннего класса представляет собой прописную букву альфа греческого алфавита. Это символ Юникода.

Ответ 5

В зависимости от того, что вам нужно, для вас может работать следующее:

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