Почему внутренние классы делают доступными частные методы?

Я не понимаю, почему это компилируется. f() и g() видны из внутренних классов, несмотря на то, что они являются частными. Специально ли они рассматриваются специально, потому что они являются внутренними классами?

Если A и B не являются статическими классами, это все равно.

class NotPrivate {
    private static class A {
        private void f() {
            new B().g();
        }
    }

    private static class B {
        private void g() {
            new A().f();
        }
    }
}

Ответ 1

(Редактирование: добавлено в ответ, чтобы ответить на некоторые комментарии)

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

Что-то вроде этого (добавляются компилятором $):

class A 
{
    private void f() 
    {
        final B b;

        b = new B();

        // call changed by the compiler
        b.$g();
    }

    // method generated by the compiler - visible by classes in the same package
    void $f()
    {
        f();
    }
}

class B
{
    private void g() 
    {
        final A a;

        a = new A();

        // call changed by the compiler
        a.$f();
    }

    // method generated by the compiler - visible by classes in the same package
    void $g()
    {
        g();
    }
}

Нестатические классы одинаковы, но у них есть добавление ссылки на внешний класс, чтобы на него можно было вызвать методы.

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

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

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

Ответ 2

Я думаю, эта цитата подводит итог:

... внутренние классы могут получить доступ ко всем членам объявляющего класса, даже к частным членам. Фактически, сам внутренний класс считается членом класса; поэтому, следуя правилам объектно-ориентированного проектирования, он должен иметь доступ ко всем членам класса.

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

Ответ 3

Java компилируется в специальных аксессорах с $в них. Таким образом, вы не можете писать Java, доступ к частным методам. Разъясняется здесь:

http://www.retrologic.com/innerclasses.doc7.html

Существует еще одна категория членов, созданных компилятором. Частный член m класса C может использоваться другим классом D, если один класс заключает другое, или если он заключен в общий класс. Поскольку виртуальная машина не знает об этой группировке, компилятор создает локальный протокол методов доступа на C, чтобы позволить D читать, писать или вызывать элемент m. Эти методы имеют имена формы доступа $0, доступ $1 и т.д. Они никогда не являются общедоступными. Методы доступа уникальны тем, что они могут быть добавлены к закрывающим классам, а не только к внутренним классам.