Java: частные синтезаторы внутреннего класса

У меня есть Outer класс, который имеет private Inner учебный класс.

В моем Outer метод класса, я создаю экземпляр Inner Класс следующим образом:

Outer outer = new Outer();
Inner inner = outer.new Inner();

Компилятор преобразует этот код в:

Outer outer = new Outer();
Inner inner = new Inner(outer, null);

Использование отражения показывает, что Inner Класс имеет следующие синтезированные конструкторы:

private Outer$Inner(Outer)
Outer$Inner(Outer,Outer$Inner)

Так как Inner класс privateКомпилятор добавляет, что private конструктор, чтобы никто не мог создать экземпляр этого класса. Но, очевидно, Outer класс должен иметь возможность его создания, поэтому компилятор добавляет этот частный конструктор другого пакета, который, в свою очередь, вызывает частный конструктор. Кроме того, так как у конструктора пакета есть $ по своему названию нормальный код Java не может вызвать его.

Вопрос: зачем синтезировать один закрытый и один частный конструктор пакетов? Почему бы не синтезировать только конструктор частного пакета и покончить с этим?

3 ответа

Решение

Если вы напишите код, как,

public class Outer {
      private class Inner {}
}

Вы заметите, что есть только один конструктор private Outer$Inner(Outer)

Этот конструктор требуется в Разделе 8.8.9 JLS, в котором говорится, что если конструктор не определен, должен быть создан конструктор по умолчанию, и в этом случае конструктор по умолчанию должен быть закрытым,

В типе класса, если класс объявлен как открытый, тогда конструктору по умолчанию неявно предоставляется модификатор доступа public (§6.6); если класс объявлен защищенным, тогда конструктору по умолчанию неявно предоставляется защищенный модификатор доступа (§6.6); если класс объявлен как приватный, тогда конструктору по умолчанию неявно предоставляется модификатор доступа private (§6.6); в противном случае конструктор по умолчанию имеет доступ по умолчанию, подразумеваемый без модификатора доступа.

Тем не менее, когда вы создаете экземпляр Inner внутри Outer с помощью кода,

public class Outer {
    private class Inner {}
        public String foo() {
            return new Inner().toString(); 
        }
}

Компилятор должен сгенерировать конструктор, который Outer может вызвать на законных основаниях (вы не можете на законных основаниях вызывать частный конструктор по умолчанию, потому что он является приватным). Таким образом, новый синтетический конструктор должен быть сгенерирован компилятором. Новый конструктор должен быть синтетическим, в соответствии с разделом 13.1 JLS

Любые конструкции, представленные компилятором, которые не имеют соответствующей конструкции в исходном коде, должны быть помечены как синтетические, за исключением конструкторов по умолчанию и метода инициализации класса.

Этот второй конструктор не имеет соответствующей конструкции в исходном коде, поэтому этот новый конструктор должен быть синтетическим. Первый приватный конструктор все еще должен быть сгенерирован, поскольку JLS требует приватного конструктора по умолчанию.

Это не ответ, который, я думаю, был хорошо покрыт мостиками. Это просто рабочий пример, который создает поведение, которое вы описываете:

public class Outer {
    private class Inner {
    }

    public static void main(String[] args) {
        printConstructors();

        //only one constructor is printed but two would appear if you 
        //uncommented the line below

        //new Outer().new Inner();
    }

    private static void printConstructors() {
        Constructor[] constructors = Outer.Inner.class.getDeclaredConstructors();
        for (Constructor c : constructors) {
            System.out.println(c.toGenericString());
        }
    }
}

Наиболее вероятный ответ - уважать то, что вы объявили в своем исходном коде. Это по-прежнему позволяет использовать приватный конструктор по мере того, как вы его объявили.

Это также позволяет избежать проверки, действительно ли частный конструктор вызывается внутри Inner учебный класс.

Другие вопросы по тегам