Почему внешние классы Java могут обращаться к закрытым членам внутреннего класса? - PullRequest
160 голосов
/ 26 ноября 2009

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

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 ]

81 голосов
/ 26 ноября 2009

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

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

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

57 голосов
/ 28 февраля 2012

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

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
    }
}
50 голосов
/ 26 ноября 2009

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

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

16 голосов
/ 28 марта 2014

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

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

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

5 голосов
/ 09 ноября 2011

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

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

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);
    }
}
3 голосов
/ 26 ноября 2009

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

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

3 голосов
/ 26 ноября 2009

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

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

1 голос
/ 08 августа 2014

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

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

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

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

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

Неограниченный доступ от вложенного кода к вложенному коду делает его по большей части довольно бесполезным для добавления модификаторов доступа к полям и методам вложенного типа. Это добавляет беспорядок и может дать ложное чувство безопасности новичкам в языке программирования Java.

0 голосов
/ 25 апреля 2018

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

class Outer{

private int a;

class Inner{
private int b=0;
}

void outMethod(){
a = new Inner().b;
}
}
0 голосов
/ 26 ноября 2009

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

...