Здравствуйте уважаемые читатели Хабра
Внутренние и вложенные классы java
02.03.2017 — 2019 год
Часть 1. Начало
Цель статьи: Рассказать о внутренних, вложенных, локальных, анонимных классах. Показать примеры их использования. Написать и протестировать классы в коде на java. Рассказать о свойствах этих классов.
Небольшое вступление. Предлагаю вашему внимаю цикл из трех статей.
В них я рассказываю о внутренних, вложенных, локальных, анонимных классах. Речь идет о терминологии и применении. Для этих статей я написал довольно много кода.
Это учебный код, а не руководство к действию. То есть сам код я написал для лучшего понимания. Также я постарался объяснить работу учебного кода. На написание публикации ушло довольно много времени. Прошу отнестись с пониманием.
Начнем с того, что же такое внутренние и вложенные классы. Посмотрим терминологию, встречающуюся в документации >>> :
Начнем немного отдаленно, так как всё это имеет непосредственное отношение к нашим вопросам. Вспомним объектно-ориентированное программирование. Отношения композиции и наследования.
В своей книге «Java 2 Руководство разработчика» Майкл Морган очень хорошо и подробно описывает взаимосвязи классов и объектов. Мы рассмотрим некоторые из них. Взаимосвязь «это — есть — то» выражается наследованием, а взаимосвязь «имеет часть» описывается композицией.
В наших примерах мы в основном рассматриваем композицию. Так как вложенные классы — это и есть часть чего-то. То есть у нас есть класс оболочка и вложенный класс определенный внутри класса оболочки. Пример композиции: машина имеет двигатель, двери, 4 колеса, корпус. И мы можем описать машину с помощью внутренних (Inner) классов.
Пример такого использования вы можете найти в книге Брюса Эккеля «Философия Java»
Есть некоторое предупреждение автора по использованию кода в таком виде:
Определение вложенных классов:
Вложенные классы применяются в тех случаях, когда нужно написать небольшой вспомогательный код для другого класса. Вложенный класс создают также, чтобы скрыть его переменные и методы от внешнего мира. Таким образом, вложенный класс еще один элегантный способ ограничения области видимости. Внутренние классы также есть смысл использовать, если предполагается, что они будут использовать элементы родителя, чтобы не передавать лишнего в конструкторах.
Пример вложенного класса вы можете увидеть в документации Оракле:
У нас нет, пока что, никакого контекста использования данной конструкции. С таким же успехом вложенный класс мы можем назвать вместо: «Вложенный класс» (NestedClass) — «Внутренний класс» InnerClass. Далее будем разбираться, в чем же отличия, и в каких контекстах используются классы. Брюс Эккель пишет в книге «Философия Java» так:
Терминология:
Существует четыре категории вложенных классов:
Причины использования вложенных классов (Nesred Classes)
Зачем использовать вложенные классы?
Причины использования вложенных классов такие. Если класс полезен только для одного другого класса, то вполне логично встроить его в этот класс и хранить их вместе. Использование вложенных классов увеличивает инкапсуляцию. Рассмотрим два класса верхнего уровня, A и B, где B нужен доступ к членам, которые иначе были бы объявлены закрытыми.
Скрывая класс «B» в пределах класса «А», члены класса «А» могут быть объявлены закрытыми, и «B» может получить доступ к ним. Кроме того, сам «B» может быть скрыт от внешнего мира.
Продемонстрируем это в коде:
Использование вложенных классов приводит к более читабельному и поддерживаемому коду:
Размещение класса ближе к тому месту, где он будет использован, делает код более читабельным.
Статические Вложенные Классы
Static Nested Classes
Причины использования статических вложенных классов такие.
Для случая, когда связь между объектом вложенного класса и объектом внешнего класса не нужна, можно сделать вложенный класс статическим(static).
Так как внутренний класс связан с экземпляром, он не может определить в себе любые статические члены.
Статические вложенные классы не имеют ограничений по объявлению своих данных и полей как static.
Из вложенного статического класса мы не имеем доступа к внешней не статической переменной внешнего класса.
Приведенный ниже код демонстрирует это:
Вывод: Мы не имеем доступа к не статическому полю внешнего класса, через статический контекст вложенного класса.
Но мы имеем доступ к приватным статическим полям внешнего класса из вложенного статичного класса.
Приведенный ниже фрагмент кода демонстрирует это:
В этом примере кода мы создали экземпляр внутреннего класса с именем «nestedObj».
То есть мы получаем доступ к приватной статической переменной внешнего класса, через экземпляр внутреннего класса. В контексте экземпляра связанного с внешним классом, у нас получился внутренний класс.
Литература
Майкл Морган. «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
Все вопросы, комментарии, дополнения, критика приветствуются.
Продолжение следует…
Часть 2 >>
Внутренние и вложенные классы java
02.03.2017 — 2019 год
Часть 1. Начало
Цель статьи: Рассказать о внутренних, вложенных, локальных, анонимных классах. Показать примеры их использования. Написать и протестировать классы в коде на java. Рассказать о свойствах этих классов.
Небольшое вступление. Предлагаю вашему внимаю цикл из трех статей.
В них я рассказываю о внутренних, вложенных, локальных, анонимных классах. Речь идет о терминологии и применении. Для этих статей я написал довольно много кода.
Это учебный код, а не руководство к действию. То есть сам код я написал для лучшего понимания. Также я постарался объяснить работу учебного кода. На написание публикации ушло довольно много времени. Прошу отнестись с пониманием.
Начнем с того, что же такое внутренние и вложенные классы. Посмотрим терминологию, встречающуюся в документации >>> :
В Java существуют 4 типа вложенных (nested) классов:Джошуа Блох:
- Статические вложенные классы
- Внутренние классы
- Локальные классы
- Анонимные (безымянные) классы
«Существуют четыре категории вложенных классов:Попытаемся разобраться, что же это такое.
»
- статический класс-член (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 вы можете посмотреть по этой ссылке: >>>
Терминология:
Существует четыре категории вложенных классов:
- Статические вложенные классы и не статические вложенные классы. Вложенные классы, объявленные статически, называются вложенными статическими классами.
- Внутренние классы — когда объект внутреннего класса связан с объектом обрамляющего класса. Не статические вложенные классы называются внутренними классами, если они связанны с внешним классом.
- Локальные классы — объявленные внутри блока кода и не являющиеся членом обрамляющего класса. В этом случае можно рассматривать класс как локальную переменную типа класс.
- Анонимные классы – наследуемые, от какого либо класса, классы в которых при объявлении не задано имя класса.
Причины использования вложенных классов (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
Брюс Эккель. «Философия 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
Все вопросы, комментарии, дополнения, критика приветствуются.
Продолжение следует…
Часть 2 >>
Комментарии (7)
ssurrokk
10.02.2019 03:41-1не удержусь, и приведу ссылку на свою хабровскую статью, тоже про разновидности классов, только с другой манерой подачи материала :) https://habr.com/ru/post/329110/
iLLuzor
10.02.2019 17:02Анонимный класс может быть не только наследуемым от другого класса(как правило, абстрактного), но и имплементировать интерфейс.
Graf54r
Вопрос, в практике кто-нибудь использовал вложенные классы(не анонимные)? Чтобы был смысл от этого. По моему это только усложняет код.
zikasak
google, например, использует. В том же ExoPlayer
BugM
Почитайте исходники JDK. Откроете для себя много нового.
Навскидку они точно есть в: String, ArrayList, HashMap. Хватит?
abar
Для Cucumber'а если надо прописать большое количество определений шагов, то удобно группировать их по классам (например «ШагиВалидации») и внутри класса ещё объединять по статическим вложенным классам (например «Формы», «ВсплывающиеСообщения», «Таблицы», и т.д.). Тогда и в IDE удобнее искать нужный класс, и при этом package не захламляется похожими названиями вроде «ШагиВалидацииТаблиц», «ШагиЗаполненияТаблиц», «ШагиНавигацииПоТаблицам»…