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

У конструкторов по умолчанию для частных внутренних классов есть формальный параметр?

Предостережение # 1: это, по сути, потенциальный двухпартер: во-первых, имеет ли конструктор для частного внутреннего класса формальный параметр? Если да, то почему JLS говорит, что нет? А если нет, то как/почему нет?

Предостережение # 2: Этот вопрос не для спекуляций. Я ищу только авторитетные ответы.

Конструкторы по умолчанию определены в JLS 8.8.9, в котором говорится (частично):

Конструктор по умолчанию не имеет формальных параметров, кроме внутреннего класса не private, где конструктор по умолчанию неявно объявляет один формальный параметр, представляющий непосредственно входящий экземпляр класса (§8.8.1, §15.9.2, §15.9.3).

(выделено курсивом)

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

На самом деле, javac, похоже, согласен со мной, что противоречит спецификации. Если я скомпилирую это:

public class Ctors {
  private class MyInner {
  }
}

... и запустите javap -c -private, тогда мы увидим конструктор с одним формальным параметром для экземпляра охватывающего класса:

$ javap -c -private Ctors\$MyInner
Compiled from "Ctors.java"
class Ctors$MyInner {
  final Ctors this$0;

  private Ctors$MyInner(Ctors);
    Code:
       0: aload_0
       1: aload_1
       2: putfield      #1        // Field this$0:LCtors;
       5: aload_0
       6: invokespecial #2        // Method java/lang/Object."<init>":()V
       9: return
}

Для справки, это на Oracle JDK 1.8.0_05.

Итак, JLS говорит, что конструктор по умолчанию для частных внутренних классов-членов не имеет формальных параметров, но javac/javap говорит, что он имеет один. (Мое понимание самого естественного способа для работы на работе также скажет, что оно должно иметь одно, за малое, что стоит.) Что правильно, и почему JLS специально исключает частные внутренние классы?

4b9b3361

Ответ 1

Существует разница между реализацией и спецификацией.

По-моему, выражение "за исключением" JLS

... кроме в неединичном внутреннем классе-члене...

плохо сформулирован.

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


Почему неявный формальный параметр требуется в неединичном внутреннем классе-члене?

Из JLS 8.8.1:

Класс участника может быть испущен компилятором, который отличается от компилятора выражения создания экземпляра класса. Следовательно, должен существовать стандартный способ для компилятора выражения create передать ссылку (представляющую непосредственно входящий экземпляр) в конструктор класса-члена

Например, если я скомпилирую этот внутренний класс с первым компилятором:

package p1;
public class Ctors {
    public class MyInner {
    }
}

если я хочу скомпилировать этот подкласс с другим компилятором:

package p2;

import p1.Ctors;

public class SubCtors {
    public SubCtors() {
        new Ctors();
    }
}

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


Почему неявно формальный параметр не требуется в неединичном внутреннем классе-члене?

Потому что неединичный внутренний класс-член всегда обращается к тому же компилятору, который его скомпилировал. Как вы показали, javac генерирует один и тот же конструктор независимо от видимости класса, но для этого не требуется. Другая реализация компилятора может выбрать другой способ.

В JLS 8.8.1 есть еще один пункт, который очень похож на ту же строку

В выражении для создания экземпляра класса для локального класса (не в статическом контексте) или анонимного класса, §15.9.2 указывает непосредственно входящий экземпляр локального/анонимного класса. Локальный/анонимный класс обязательно испускается тем же компилятором, что и выражение создания экземпляра класса. Этот компилятор может представлять собой тот экземпляр, который будет немедленно включен, как он пожелает. Не требуется, чтобы язык программирования Java неявно объявлял параметр в локальном/анонимном конструкторе класса.