Почему внешние классы Java могут получить доступ к закрытым членам внутреннего класса?


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

class ABC{
    class XYZ{
        private int x=10;
    }

    public static void main(String... args){
        ABC.XYZ xx = new ABC().new XYZ();
        System.out.println("Hello :: "+xx.x); ///Why is this allowed??
    }
}

Почему такое поведение разрешено?

10 149

10 ответов:

Внутренний класс-это просто способ четко отделить некоторую функциональность, которая действительно принадлежит исходному внешнему классу. Они предназначены для использования, когда у вас есть 2 Требования:

  1. некоторая часть функциональности в вашем внешнем классе была бы наиболее ясной, если бы она была реализована в отдельном классе.
  2. несмотря на то, что он находится в отдельном классе, функциональность очень тесно связана с тем, как работает внешний класс.

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

Если вы хотите скрыть закрытые члены вашего внутреннего класса, вы можете определить интерфейс с открытыми членами и создать анонимный внутренний класс, который реализует этот интерфейс. Пример ниже:

class ABC{
    private interface MyInterface{
         void printInt();
    }

    private static MyInterface mMember = new MyInterface(){
        private int x=10;

        public void printInt(){
            System.out.println(String.valueOf(x));
        }
    };

    public static void main(String... args){
        System.out.println("Hello :: "+mMember.x); ///not allowed
        mMember.printInt(); // allowed
    }
}

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

Это реализуется с помощью синтетических методов, защищенных от пакетов: внутренний класс будет скомпилирован в отдельный класс в том же пакете (ABC$XYZ). JVM не поддерживает этот уровень изоляции напрямую, так что на уровне байт-кода ABC$XYZ будет иметь методы, защищенные пакетами, которые внешний класс использует для доступа к закрытому классу. методы / поля.

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

Там говорится, что есть определение частной области видимости на JLS-определение доступности :

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

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

В отличие отabyx объявление класса static не изменяет ограничений доступа к заключающему классу, как показано ниже. Также работают ограничения доступа между статическими классами в одном и том же заключающем классе. Я был удивленный...

class MyPrivates {
    static class Inner1 { private int test1 = 2; }
    static class Inner2 { private int test2 = new Inner1().test1; }

    public static void main(String[] args) {
        System.out.println("Inner : "+new Inner2().test2);
    }
}

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

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

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

Если в вашем случае нет смысла, чтобы классы могли видеть внутреннюю работу друг друга - что в основном означает, что внутренний класс мог бы быть просто обычным классом, вы можете объявить внутренний класс как static class XYZ. Использование static это будет означать, что они не будут делиться состоянием (и, например, new ABC().new XYZ() не будет работать, и вам нужно будет использовать new ABC.XYZ().
Но, если это так, вы должны подумать о том, действительно ли XYZ должен быть внутренним классом и что, возможно, он заслуживает своего собственного файла. Иногда имеет смысл создать статический внутренний класс (например, если вам нужен небольшой класс, который реализует интерфейс, используемый вашим внешним классом, и это больше нигде не поможет). Но примерно в половине случаев это должно было быть сделано внешний класс.

Тило добавил Хороший Ответ на ваш первый вопрос "как это возможно?". Я хочу немного подробнее остановиться на втором заданном вопросе: почему такое поведение разрешено?

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

Так почему же тогда? Я думаю, что пример лучше иллюстрирует этот момент.

Подумайте о своем теле и своем мозге. Если вы вводите героин в руку, ваш мозг получает кайф. Если область миндалевидного тела вашего мозга видит то, что он считает угрозой для вашей личной безопасности, скажем, Оса, например, он заставит ваше тело повернуться в другую сторону и убежать в горы без посторонней помощи. Ты" думаешь " об этом дважды. Итак, мозг является неотъемлемой частью тела - и, как ни странно,наоборот. Использование контроля доступа между такими тесно связанными субъектами лишает их притязаний на родство. Если вам действительно нужен контроль доступа, то вам нужно разделить классы на действительно различные единицы. До тех пор они-одна и та же единица. Движущим примером для дальнейших исследований было бы взглянуть на то, как Java Iterator обычно это так выполненный. Неограниченный доступ от заключающего кода к вложенному коду делает, по большей части, довольно бесполезным добавление модификаторов доступа к полям и методам вложенного типа. Это добавляет беспорядок и может создать ложное чувство безопасности для новых пользователей языка программирования Java.

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

class Outer{

private int a;

class Inner{
private int b=0;
}

void outMethod(){
a = new Inner().b;
}
}

Потому что ваш метод main() находится в классе ABC, которыйможет получить доступ к своему собственному внутреннему классу.