Внутренние и вложенные классы java. Часть 2

    Внутренние и вложенные классы java. Часть 2

    02.03.2017 — 2019 год

    <<< Часть 1
    Часть 3 >>>

    Часть 2

    Внутренние классы
    todo: Раскрыть более подробно области применения. Добавить яркие ассоциации для лучшего понимания материала. Добавить иллюстрации.

    Inner Classes — Внутренние классы

    Внутренний класс связан с экземпляром его обрамляющего класса (из документации).

    Пример внутреннего класса есть в документации.

    Создадим класс:

    /* Пример №7 */
     
    class OuterClass {
        ...
        class InnerClass {
            ...
        }
    }
     

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

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

     OuterClass.InnerClass innerObject = outerObject.new InnerClass(); 


    Пример:

    
     /* Пример №8 файл Outer.java*/
    package inner;
    
    /**
     *
     * @author Ar20L80
     */
    public class Outer {
        
       
        class InnerClass {
            
        }
        Outer(){}
        
        public static void main(String[] args) {
        Outer outerObject = new Outer();
        Outer.InnerClass innerObject = outerObject.new InnerClass(); // создание экземпляра 
     внутреннего класса
        }
    }
     


    По-другому мы можем написать так:

    /*
    Учебный пример №9
    файл Outer5.java
    Внутренние классы
    Получить ссылку на внешний класс в конструкторе внутреннего
     */
    package inner;
    
    /**
     *
     * @author Ar20L80
     */
    public class Outer5 {
           class Inner5{
                private  Outer5 myOuter;
                Inner5(){
                 myOuter = Outer5.this;
                }
           }
      public static void main(String[] args){
      Outer5 outer5 = new Outer5();
      }
    }
    


    Обратите внимание на запись вида myOuter = Outer5.this;. Она означает получение ссылки на текущий экземпляр внешнего класса Outer5.this.

    Рассмотрим свойства внутренних классов.

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

    Внутренний класс стоит использовать, когда нам нужна инкапсуляция. Во внутреннем классе мы, таким образом закрываем всё от «внешнего мира».

    Например, Map.Entry — нигде кроме интерфейса Map и его реализаций он не используется. Смотрите исходный код Map.Entry и Map. Это я привел только один пример.

    Далее рассмотрим пример явного наследования.

    /*
    Учебный пример №10(1).
    файл Outer6.java
    Внутренние классы
    */
    package inner;
    
    /**
     *
     * @author Ar20L80
     */
    class AnyClass{} // класс от которого наследуем Inner6
    
    public class Outer6 { // внешний класс
        class Inner6 extends AnyClass{ // внутренний класс явно унаследован от "прилегающего"
        // тут мы унаследовали внутренний класс от  AnyClass{}
        // и можем расширить функциональность класса AnyClass{} 
        // и класса Outer6
        }
    }
    


    В этом примере у нас, по сути, получилось множественное наследование, и мы можем использовать функционал класса AnyClass и функционал класса Outer6.

    Диаграмма наследования:


    рис. 1

    Здесь модификатор доступа у класса Outer6 по умолчанию. То есть класс Outer6 виден только в нашем пакете (package inner). Класс Inner6 закрыт от внешнего мира и внешнего воздействия.
    То есть более «защищен».
    Это только пример множественного наследования от «прилегающего» класса и класса «оболочки». На практике вам вряд-ли такое понадобится. Тут я рассматриваю такую возможность только в учебных целях и для лучшего понимания.

    Замечание Выражение: «прилегающего» класса — взято из книги «Философия Java».

    Дополним пример.

    /*
    Учебный пример №11
    файл Outer7.java
     Внутренние классы
    
     */
    package inner;
    
    /**
     *
     * @author Ar20L80
     */
    class AnyClass2{
    void anyClass2Method(){}
    }
    public class Outer7 {
        private int iOuterVar;
        private class Inner7 extends AnyClass2
        {
            private Outer7 out7;
    
            public Inner7() {
                out7 = Outer7.this; // ссылка на окружающий класс
            }
            
            private int anyMethodOfInner7(){
            
                super.anyClass2Method();// можем вызвать метод нашего супер класса AnyClass2
                return out7.iOuterVar; // можем обратиться к переменным 
                // и методам Outer7
               
            }
        
        }
    }
    






    В этом примере видно, что мы можем использовать как поля и методы «окружающего» класса — Outer7, так поля и методы того класса, от которого мы наследовали внутренний класс — AnyClass2. Это дает нам несколько большие возможности и гибкость при использовании внутреннего класса. Хотя для множественного наследования более подходят интерфейсы.

    Совет из книги «Философия Java. Брюс Эккель. ISBN 5-272-00250-4» c. 313:
    «Каждый внутренний класс может независимо наследовать определенную реализацию.

    Внутренний класс не ограничен при наследовании в ситуациях, где внешний класс уже наследует реализацию.»


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

    Объект внутреннего класса сохраняет информацию о месте, где он был создан.

    Продолжение следует…
    Часть 3 >>>

    Литература

    Майкл Морган. «Java 2.Руководство разработчика» ISBN 5-8459-0046-8
    Брюс Эккель. «Философия Java.» ISBN 5-272-00250-4
    Герберт Шилдт «Java. Полное руководство. 8-е издание.» ISBN: 978-5-8459-1759-1

    Ссылки:

    ru.wikipedia.org
    src-code.net/lokalnye-vnutrennie-klassy-java

    Документация Oracle
    Support the author
    Share post

    Similar posts

    Comments 12

      –1
      Странно, что минусуют, мне было полезно и интересно почитать, ибо скоро экзамен по Java. Вот такие статьи очень хорошо помогают посмотреть со стороны на определенную тему и, возможно, что-то новое подметить. Можно только пожелать удачи автору :)
        +3
        У автора адище с оформлением постов и кода, да и тему он размазал на несколько постов, хотя она отлично умещалась в одном.
          +5
          Потому что статья, во-первых, неполная, во-вторых, содержит крайне сомнительные идеи про множественное наследование, в-третьих, является кратким пересказом соответствующей главы учебника.
            0
            Не советую читать такое перед экзаменом. Почитайте лучше оригинал. Родная документация сильно лучше — а это не более чем ее кривой пересказ.
            –3
            Спасибо, учту ваши пожелания.

              +1
              >Например, Map.Entry — нигде кроме интерфейса Map и его реализаций он не нужен.

              Во-первых, Map.Entry это интерфейс, а не класс. Во-вторых, он часть внешнего API Map. И нужен всем, кто использует например entrySet().

              Плохой пример, негодный.
                +1
                Я вообще не врубился в чем разница между «вложенными» и «внутренними» классами. Возможно какая-то неточность изложения?
                Я ни разу на Java ничего не писал, поэтому могу ошибаться:) Но ИМХО:
                Есть статическая вложенность, то есть объемлющие классы используются просто как пространства имен. Логично, что никакого доступа к нестатическим полям из статически вложенного класса быть не может. Полная аналогия со статическими методами.
                И есть нестатическая вложенность. Для нее по идее внутри каждого экземпляра вложенного класса должно быть скрытое поле — указатель/ссылка на объект объемлющего класса, к которому относится данный вложенный класс (отсюда и создание через конструкцию outerObject.new InnerClass() ). Это полная аналогия с обычными методами, которые принимают неявный параметр this — указатель/ссылку на объект класса, для которого вызывается метод (отсюда констукция Outer7.this).
                Кстати, интересно — есть ли в Java ключевое слово, само по себе обозначающее текущий объемлющий класс или сразу ссылку на его контекст (по аналогии с this и super)?.. Было бы логично, но я ничего подобного не встречал.
                  +1
                  1. Всё верно.
                  2. Есть уродская синтаксическая конструкция OutterClass.this, делающая боль при рефакторинге.
                    0
                    Кстати интересно, в каких еще языках есть такая возможность.
                    В С++ точно нет, там вложенность классов это просто просто использование объемлющего класса как пространства имен.
                    В C# вроде тоже нет.
                    Но поле — ссылку/указатель на объемлющий класс можно объявлять во вложенном классе явно и явно передавать в конструктор вложенного класса, т.е. можно явно сэмулировать поведение как в java.
                    В D вроде бы есть, и вроде аналогично Java.

                    А вот ключевого слова (аналогичного this) для прямого доступа к объекту объемлющего класса вроде как нигде нет. Какое слово лучше всего подошло бы?
                    И вообще удобна ли такая логика по умолчанию, или лучше как в С++/C# (только пространства имен)? Что скажут люди, имеющие опыт с Java?
                    +1
                    Нестатический вложенный класс называют внутренним (inner).
                      0
                      Просто и понятно!
                    –1
                    NeoCode, я услышал ваши замечания.

                    Only users with full accounts can post comments. Log in, please.