Хабрахабр

[recovery mode] Внутренние и вложенные классы java. Часть 1

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

03. 02. 2017 — 2019 год

Начало Часть 1.

Показать примеры их использования. Цель статьи: Рассказать о внутренних, вложенных, локальных, анонимных классах. Рассказать о свойствах этих классов. Написать и протестировать классы в коде на java.

Посмотрим терминологию, встречающуюся в документации >>> : Начнем с того, что же такое внутренние и вложенные классы.

В Java существуют 4 типа вложенных (nested) классов:

  1. Статические вложенные классы
  2. Внутренние классы
  3. Локальные классы
  4. Анонимные (безымянные) классы

Джошуа Блох:

«Существуют четыре категории вложенных классов:

  • статический класс-член (static member class),
  • не статический класс-член (nonstatic member class),
  • анонимный класс (anonymous class)
  • и локальный класс (local class).

»

Попытаемся разобраться, что же это такое.

Вспомним объектно-ориентированное программирование. Начнем немного отдаленно, так как всё это имеет непосредственное отношение к нашим вопросам. Отношения композиции и наследования.

Мы рассмотрим некоторые из них. В своей книге «Java 2 Руководство разработчика» Майкл Морган очень хорошо и подробно описывает взаимосвязи классов и объектов. Взаимосвязь «это — есть — то» выражается наследованием, а взаимосвязь «имеет часть» описывается композицией.

Так как вложенные классы — это и есть часть чего-то. В наших примерах мы в основном рассматриваем композицию. Пример композиции: машина имеет двигатель, двери, 4 колеса, корпус. То есть у нас есть класс оболочка и вложенный класс определенный внутри класса оболочки. И мы можем описать машину с помощью внутренних (Inner) классов.

Пример такого использования вы можете найти в книге Брюса Эккеля «Философия Java»

/* Пример №1 */
//: c06:Car.java
// композиция с использованием открытых объектов // двигатель class Engine{
public void start()
public void rev(){}
public void stop(){}
}
class Wheel{
public void inflare(int psi){}// накачать
}
// окно
class Window{
public void rollup(){}// приоткрыть
public void rolldown(){}// опустить
} // дверь
class Door{
public Window window=new Window();
public void open(){}//открыть public void close(){}// закрыть
} // машина public class Car{
public Engine engine = new Engine();
public Wheel[] wheel = new Wheel[4];
public Door left = new Door(),
right = new Door();//две двери
public Car(){
for(int i = 0; i<4; i++) wheel[i]= new Wheel();
}
public static void main(String[] args){
Car car= new Car();
car.left.window.rollup();
car.wheel[0].inflare(72);
}
}

Есть некоторое предупреждение автора по использованию кода в таком виде:

Однако нужно помнить, что описанный случай является особым, и в основном поля класса нужно объявлять как private." " Так как композиция объекта является частью проведенного анализа задачи (а не
просто частью реализации класса), объявление членов класса открытыми, помогает программисту-клиенту понять, как использовать класс, и упрощает создателю написание кода.

Статические вложенные классы

Определение вложенных классов:

То есть класс просто определен внутри другого, даже не важно статически определен или не статически. Класс называется вложенным (nested), если он определен внутри другого класса. Если вложенный класс оказывается полезен в каком-либо ином контексте, он должен стать классом верхнего уровня. Вложенный класс создается для того, чтобы обслуживать окружающий его класс.

Применение

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

Пример вложенного класса вы можете увидеть в документации Оракле:

/* Пример №2 */
//
class OuterClass { ... class NestedClass { ... }
}

У нас нет, пока что, никакого контекста использования данной конструкции. С таким же успехом вложенный класс мы можем назвать вместо: «Вложенный класс» (NestedClass) — «Внутренний класс» InnerClass. Далее будем разбираться, в чем же отличия, и в каких контекстах используются классы. Брюс Эккель пишет в книге «Философия Java» так:

Класс называется вложенным (nested), если он определен внутри другого класса"

Документацию Oracle вы можете посмотреть по этой ссылке: >>>

Терминология:

Существует четыре категории вложенных классов:

  1. Статические вложенные классы и не статические вложенные классы. Вложенные классы, объявленные статически, называются вложенными статическими классами.
  2. Внутренние классы — когда объект внутреннего класса связан с объектом обрамляющего класса. Не статические вложенные классы называются внутренними классами, если они связанны с внешним классом.
  3. Локальные классы — объявленные внутри блока кода и не являющиеся членом обрамляющего класса. В этом случае можно рассматривать класс как локальную переменную типа класс.
  4. Анонимные классы – наследуемые, от какого либо класса, классы в которых при объявлении не задано имя класса.

Причины использования вложенных классов (Nesred Classes)
Зачем использовать вложенные классы?

Если класс полезен только для одного другого класса, то вполне логично встроить его в этот класс и хранить их вместе. Причины использования вложенных классов такие. Рассмотрим два класса верхнего уровня, A и B, где B нужен доступ к членам, которые иначе были бы объявлены закрытыми. Использование вложенных классов увеличивает инкапсуляцию.

/* Пример №3 */
//
class A{ ... class B { ... }
}

Кроме того, сам «B» может быть скрыт от внешнего мира. Скрывая класс «B» в пределах класса «А», члены класса «А» могут быть объявлены закрытыми, и «B» может получить доступ к ним.

Продемонстрируем это в коде:

/* Пример №4 */
package innernested; /** * * @author Ar20L80 */
public class A { private static int iPrivVar; class B { void setPrivateOfA(int var) { A.iPrivVar = var; } }
}

Использование вложенных классов приводит к более читабельному и поддерживаемому коду:
Размещение класса ближе к тому месту, где он будет использован, делает код более читабельным.

Статические Вложенные Классы
Static Nested Classes

Причины использования статических вложенных классов такие.

Для случая, когда связь между объектом вложенного класса и объектом внешнего класса не нужна, можно сделать вложенный класс статическим(static).

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

Статические вложенные классы не имеют ограничений по объявлению своих данных и полей как static.

Из вложенного статического класса мы не имеем доступа к внешней не статической переменной внешнего класса.

Приведенный ниже код демонстрирует это:

/* Пример №5 Статические вложенные классы Попытка доступа к нестатической переменной внешнего класса Outer2 через обращение из вложенного статического класса Nested2 */
package nested; /** * * @author Ar20L80 * 20.03.2016 */
public class Outer2 { public int pubOutVar; // переменная не статическая и мы не имеем к ней доступа // из внутреннего статического класса private int prOutVar; public Outer2(){} static class Nested2{ public static int pub_innVar; // тут все в порядке public Nested2() {} int getOuterPublicVariable() { return Outer2.this.pubOutVar; // ошибка return Outer2.pubOutVar; // ошибка } int getOuterPrivateVariable() { return Outer2.this.prOutVar; // ошибка return Outer2.prOutVar; // ошибка } }
}
/*
вывод программы:
программа не компилируется
*/

Вывод: Мы не имеем доступа к не статическому полю внешнего класса, через статический контекст вложенного класса.

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

Приведенный ниже фрагмент кода демонстрирует это:

/* Пример №6 Статические вложенные классы Демонстрация доступа к «приватной» статической переменной внешнего класса из внутреннего статического класса 20.03.2016 */
package nested; /** * * @author Ar20L80 */
public class Outer3 { private static int prStOuterVar; public Outer3(){} static class Nested3 // Nested { int getStaticOuterVar() { return Outer3.prStOuterVar; // ok } void setStaticOuterVariable(int var) { Outer3.prStOuterVar = var; // ok } } public static void main(String[] args) { Outer3.Nested3 nestedObj = new Outer3.Nested3(); // экземпляр класса внутренний Outer3.prStOuterVar = 19; System.out.println("nestedObj.getStaticOuterVar() = "+nestedObj.getStaticOuterVar());//статическая переменная внешнего класса из экземпляра внутреннего // устанавливаем через экземпляр внутреннего класса nestedObj.setStaticOuterVariable(77); System.out.println("Outer3.prStOuterVar = "+ Outer3.prStOuterVar); }
}
/*
Вывод программы:
nestedObj.getStaticOuterVar() = 19
Outer3.prStOuterVar = 77
*/

В этом примере кода мы создали экземпляр внутреннего класса с именем «nestedObj».
То есть мы получаем доступ к приватной статической переменной внешнего класса, через экземпляр внутреннего класса. В контексте экземпляра связанного с внешним классом, у нас получился внутренний класс.

Литература

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

Ссылки:

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

Документация Oracle

Все вопросы, комментарии, дополнения, критика приветствуются.

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

Теги
Показать больше

Похожие статьи

Добавить комментарий

Ваш e-mail не будет опубликован. Обязательные поля помечены *

Кнопка «Наверх»
Закрыть