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

StackOverflow https://stackoverflow.com/questions/1801718

Вопрос

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

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??
    }
}

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

Это было полезно?

Решение

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

      

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

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

    В противоречие с заголовком 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);
        }
    }
    

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

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

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

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

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

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

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

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

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

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

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

    class Outer{
    
    private int a;
    
    class Inner{
    private int b=0;
    }
    
    void outMethod(){
    a = new Inner().b;
    }
    }
    

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

    Лицензировано под: CC-BY-SA с атрибуция
    Не связан с StackOverflow
    scroll top