Подтвердить что ты не робот

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 должен иметь возможность создавать экземпляр, поэтому компилятор добавляет другой конструктор частного пакета, который, в свою очередь, вызывает частный конструктор. Кроме того, поскольку конструктор package-private имеет это $ в своем имени, нормальный код Java не может его вызывать.

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

4b9b3361

Ответ 1

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

public class Outer {
      private class Inner {}
}

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

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

В классе, если класс объявлен общедоступным, то по умолчанию конструктор неявно получает общедоступный модификатор доступа (§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 требуется собственный конструктор по умолчанию.

Ответ 2

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

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

Ответ 3

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

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