СоХабр закрыт.

С 13.05.2019 изменения постов больше не отслеживаются, и новые посты не сохраняются.

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

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

02.03.2017 — 2019 год

<<< Часть 1

Часть 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();
  }
}

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

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

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

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


/*
Учебный пример №10  .
Внутренние классы
*/
package inner;

/**
 *
 * @author Ar20L80
 */
class AnyClass{} // класс от которого наследуем Inner6

public class Outer6 {
    class Inner6 extends AnyClass{
    // тут мы унаследовали внутренний класс от  AnyClass{}
    // и можем расширить функциональность класса AnyClass{} 
    // и класса Outer6
    }
}

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

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


/*
Учебный пример №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
+13
~4100

комментарии (11)

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

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

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

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