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

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

    02.03.2017 — 2019 год

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

    Часть 2

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

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

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

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

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

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

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

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

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

    Пример:

    * Пример №8 */
    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
    
    Внутренние классы
    Получить ссылку на внешний класс в конструкторе внутреннего
     */
    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  .
    Внутренние классы
    */
    package inner;
    
    /**
     *
     * @author Ar20L80
     */
    class AnyClass{} // класс от которого наследуем Inner6
    
    public class Outer6 { // внешний класс
        class Inner6 extends AnyClass{ // внутренний класс явно унаследован от "прилегающего"
        // тут мы унаследовали внутренний класс от  AnyClass{}
        // и можем расширить функциональность класса AnyClass{} 
        // и класса Outer6
        }
    }
    

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

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

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

    
    /*
    Учебный пример №11
     Внутренние классы
    
     */
    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
    Поделиться публикацией

    Комментарии 11

      –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
                      Просто и понятно!

                  Только полноправные пользователи могут оставлять комментарии. Войдите, пожалуйста.

                  Самое читаемое