Учебник Oracle Java - статические классы - возможная ошибка в учебнике

Я новичок в Java, изучая Java из учебника Oracle Java. Теперь я узнаю о вложенных классах, статических классах и внутренних классах. Я нашел следующее объяснение, которое кажется странным, и я думаю, что это неправильно.

От: https://docs.oracle.com/javase/tutorial/java/javaOO/nested.html

Вложенный класс является членом его охватывающего класса. Нестатические вложенные классы (внутренние классы) имеют доступ к другим членам охватывающего класса, даже если они объявлены частными. Статические вложенные классы не имеют доступа к другим членам входящего класса

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

Примечание. Статический вложенный класс взаимодействует с членами экземпляра его внешнего класса (и других классов), как и любой другой класс верхнего уровня. По сути, статический вложенный класс поведенчески является классом верхнего уровня, который был вложен в другой класс верхнего уровня для удобства упаковки.

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

public class A {

    private int x;
    static private int y;


    static public class B{

        static void doSomething(){
            y++;
            System.out.println("y is now " + y );
        }

        static void doSomethingElse(A a)
        {
            a.x++;
            System.out.println("a.x is " + a.x );
        }
    }
}

// ------

public class Main {

    public static void main(String[] args){
        A a = new A();
        A.B b = new A.B();
        b.doSomething();
        b.doSomethingElse(a);
    }
}

Это ошибка в учебнике, или я, может быть, не понимаю что-то хорошее? Спасибо

Ответ 1

Это ошибка в учебнике, или я, может быть, не понимаю что-то хорошее?

Вы прекрасно понимаете. В лучшем случае страница руководства вводит в заблуждение.

Здесь есть два отдельных понятия:

  1. Имеете ли вы разрешение на доступ к предмету в рамках правил управления доступом Java (например, private, package-private, protected, public).

  2. Значение "статический". Экземпляр "внутреннего" вложенного класса всегда связан с экземпляром охватывающего класса (сохраняя ссылку на экземпляр охватывающего класса в скрытом поле экземпляра внутреннего класса). У "статического" вложенного класса этого нет.

На странице руководства запутаны два понятия.

Вложенный класс является членом его охватывающего класса.

Ага.

Нестатические вложенные классы (внутренние классы) имеют доступ к другим членам охватывающего класса, даже если они объявлены частными. Статические вложенные классы не имеют доступа к другим членам входящего класса.

Нету.

Поставляя экземпляр самостоятельно, вы видите, что статические классы действительно имеют доступ к членам охватывающего класса, включая поля частных экземпляров, поэтому почему a.x++; в вашем примере компилируется. Этот доступ.

Используя слова "доступ" и "частный", параграф настоятельно предлагает говорить о контроле доступа в определении, указанном в Спецификации языка Java. Но это не так. Он только пытается объяснить понятие № 2 о том, как экземпляры охватывающих классов связаны с вложенными классами. И даже тогда это все еще неправильно, потому что статические вложенные классы, безусловно, имеют доступ к статическим членам охватывающего класса, о которых в абзаце говорится, что они этого не делают. Тот, кто написал эту страницу, был неаккуратным.

Примечание. Статический вложенный класс взаимодействует с членами экземпляра его внешнего класса (и других классов), как и любой другой класс верхнего уровня. По сути, статический вложенный класс поведенчески является классом верхнего уровня, который был вложен в другой класс верхнего уровня для удобства упаковки.

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


Вот правильное правило контроля доступа, данное JLS§6.6.1. Определение доступности:

[Если] член или конструктор объявлен private, доступ [..] разрешен только в том случае, если он встречается внутри тела класса верхнего уровня (§7.6), который включает объявление члена или конструктора.

Это определение на удивление коротко, но оно охватывает все, что здесь имеет значение.

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

Кроме того, все вложенные классы также имеют доступ ко всем членам и конструкторам всех других вложенных классов в одном классе верхнего уровня.

И класс верхнего уровня имеет доступ ко всем членам и конструкторам всех классов, вложенных в него.

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

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

Ответ 2

Это ошибка в учебнике, или, может быть, я плохо что-то понимаю?

Ошибка в вашем понимании, и учебники являются правильными. Нигде внутри вашего вложенного статического класса нет никаких прямых манипуляций с полями экземпляра внешнего класса. Я говорю об этих полях без прилагаемого экземпляра - нигде вы не можете напрямую манипулировать x не привязывая его к экземпляру A

Таким образом, вы можете сделать это:

static void doSomethingElse(A a) {
    a.x++;  // x is part of the A instance passed into a parameter
    System.out.println("a.x is " + a.x );
}

но вы не можете этого сделать:

static void doSomethingElse2() {
    x++;
    System.out.println("x is " + x );
}

И этот код будет таким же, если B был статическим вложенным или автономным не-вложенным классом.


Ты спрашиваешь:

"Статический вложенный класс взаимодействует с членами экземпляра его внешнего класса, как и любой другой класс верхнего уровня"?

Точно так же, как показано выше, нестатический вложенный класс может напрямую взаимодействовать с полем a (как doSomethingElse2()) без необходимости поддержки экземпляра A, в то время как статический вложенный класс и отдельный класс не могут. Они оба требуют отдельный экземпляр A, здесь, который передается в ваш параметр метода doSomethingElse(A a).


Основное отличие между статическим вложенным и автономным состоит в том, что первый, вложенный класс, имеет доступ к закрытым членам внешнего класса, а автономный - нет. Возможно, это ваш источник путаницы.