что такое внутренний класс java
Внутренние и вложенные классы java. Часть 1
Внутренние и вложенные классы java. Часть 1
02.03.2017 — 2019 год
Цель статьи: Рассказать о внутренних, вложенных, локальных, анонимных классах. Показать примеры их использования. Написать и протестировать классы в коде на java. Рассказать о свойствах этих классов. Материал предназначен для лучшего понимания безымянных классов, лямбда выражений, адаптеров и многопоточности. То есть перед их изучением.
Небольшое вступление. Предлагаю вашему вниманию цикл из трех статей.
В них я рассказываю о внутренних, вложенных, локальных, анонимных классах. Речь идет о терминологии и применении. Для этих статей я написал довольно много кода.
Это учебный код, а не руководство к действию. То есть сам код я написал для лучшего понимания. Также я постарался объяснить работу учебного кода. На написание данной публикации, ушло довольно много времени. Публикация состоит из трех частей. Прошу отнестись с пониманием.
Для лучшего изучения материала у вас должна быть некоторая подготовка.
То есть вам нужно знать: синтаксис языка java, область видимости переменных, классы, статические и нестатические члены класса, создание экземпляров класса, наследование, модификаторы доступа.
Начнем с того, что же такое внутренние и вложенные классы. Посмотрим терминологию, встречающуюся в документации и литературе:
В Java существуют 4 типа вложенных (nested) классов:
Существуют четыре категории вложенных классов:
Попытаемся разобраться, что же это такое.
Начнем немного отдаленно, так как всё это имеет непосредственное отношение к нашим вопросам. Вспомним объектно-ориентированное программирование. Отношения композиции и наследования.
В своей книге «Java 2 Руководство разработчика» Майкл Морган очень хорошо и подробно описывает взаимосвязи классов и объектов. Мы рассмотрим некоторые из них. Взаимосвязь «это — есть — то» выражается наследованием, а взаимосвязь «имеет часть» описывается композицией.
В наших примерах мы в основном рассматриваем композицию. Так как вложенные классы — это и есть часть чего-то. То есть у нас есть класс оболочка и вложенный класс определенный внутри класса оболочки. Пример композиции: машина имеет двигатель, двери, 4 колеса, корпус. И мы можем описать машину с помощью внутренних (Inner) классов.
Пример такого использования вы можете найти в книге Брюса Эккеля «Философия Java»
Есть некоторое предупреждение автора по использованию кода в таком виде:
Так как композиция объекта является частью проведенного анализа задачи (а не
просто частью реализации класса), объявление членов класса открытыми, помогает программисту-клиенту понять, как использовать класс, и упрощает создателю написание кода. Однако нужно помнить, что описанный случай является особым, и в основном поля класса нужно объявлять как private.
Выше я не случайно упомянул наследование и композицию. Это напрямую относится к дальнейшему материалу.
Статические вложенные классы
Определение вложенных классов:
Класс называется вложенным (nested), если он определен внутри другого класса.
То есть класс просто определен внутри другого, даже не важно статически определен или не статически. Вложенный класс создается для того, чтобы обслуживать окружающий его класс. Если вложенный класс оказывается полезен в каком-либо ином контексте, он должен стать классом верхнего уровня.
Вложенные классы применяются в тех случаях, когда нужно написать небольшой вспомогательный код для другого класса. Вложенный класс создают также, чтобы скрыть его переменные и методы от внешнего мира. Таким образом, вложенный класс еще один элегантный способ ограничения области видимости. Внутренние классы также есть смысл использовать, если предполагается, что они будут использовать элементы родителя, чтобы не передавать лишнего в конструкторах.
Пример вложенного класса вы можете увидеть в документации Оракле:
У нас нет, пока что, никакого контекста использования данной конструкции. С таким же успехом вложенный класс мы можем назвать вместо: «Вложенный класс» (NestedClass) — «Внутренний класс» InnerClass. Далее будем разбираться, в чем же отличия, и в каких контекстах используются классы. Брюс Эккель пишет в книге «Философия Java» так:
«Класс называется вложенным (nested), если он определен внутри другого класса»
Документацию Oracle вы можете посмотреть по этой ссылке: >>>
Существует четыре категории вложенных классов:
Причины использования вложенных классов такие. Если класс полезен только для одного другого класса, то вполне логично встроить его в этот класс и хранить их вместе. Использование вложенных классов увеличивает инкапсуляцию. Рассмотрим два класса верхнего уровня, 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
Все вопросы, комментарии, дополнения, критика приветствуются.
Продолжение следует…
Часть 2 >>>
Если у вас есть возможность, вам пригодилось, и вы можете помочь, то нажмите кнопку поддержать автора материально.
Внутренние классы в Java (inner classes java)
На лицо ужасные, добрые внутри
(c) Бриллиантовая рука
Статические вложенные классы (static nested classes)
Статические вложенные классы, не имеют доступа к нестатическим полям и методам обрамляющего класса, что в некотором роде аналогично статическим методам, объявленным внутри класса. Доступ к нестатическим полям и методам может осуществляться только через ссылку на экземпляр обрамляющего класса. В этом плане static nested классы очень похожи на любые другие классы верхнего уровня.
Кроме этого, static nested классы имеют доступ к любым статическим методам внешнего класса, в том числе и к приватным.
Польза данных классов заключается в основном в логической группировке сущностей, в улучшении инкапсуляции, а также в экономии class-space.
Давайте рассмотрим такой пример:
Клиентский код будет выглядеть, например, так:
Внутренние классы-члены (member inner classes)
Внутренние классы в Java делятся на такие три вида:
Внутренние классы-члены ассоциируются не с самим внешним классом, а с его экземпляром. При этом они имеют доступ ко всем его полям и методам. Например:
В этом примере мы имеем внутренний класс Query, который предназначен для поиска пользователей по заданным параметрам. Класс Query может инкапсулировать в себе, например, работу с базой данных. При этом он имеет доступ к состоянию класса Users. Пример клиентского кода:
Если бы конструктор класса Query был объявлен как public, создать экземпляр класса Query снаружи можно было бы только через инстанс обрамляющего класса:
Обращаю ваше внимание на то, что inner class не может иметь статических объявлений. Вместо этого можно объявить статические методы у обрамляющего класса. Кроме этого, внутри таких классов нельзя объявлять перечисления.
на самом деле будет интерпретироваться так:
то есть неявно будет добавлен модификатор static.
Как было сказано выше, member inner class имеет доступ ко всем полям и методам обрамляющего класса. Давайте рассмотрим такой фрагмент кода:
Локальные классы (local inner classes)
Локальные классы (local classes) определяются в блоке Java кода. На практике чаще всего объявление происходит в методе некоторого другого класса. Хотя объявлять локальный класс можно внутри статических и нестатических блоков инициализации.
Пример использования локального класса:
Данный код с некоторыми изменениями взят из реального проекта и используется для обработки get запросов к веб-серверу. Он вводит новую абстракцию, с которой удобно работать в пределах метода и которая не нужна за его пределами.
Как и member классы, локальные классы ассоциируются с экземпляром обрамляющего класса и имеют доступ к его полям и методам. Кроме этого, локальный класс может обращаться к локальным переменным и параметрам метода, если они объявлены с модификатором final.
У локальных классов есть множество ограничений:
Кстати, интерфейсы тоже нельзя объявлять локально по тем же причинам, по каким их нельзя объявлять внутренними.
Анонимные классы (anonymous inner classes)
Классический пример анонимного класса:
На основании анонимного класса создается поток и запускается с помощью метода start класса Thread. Синтаксис создания анонимного класса базируется на использовании оператора new с именем класса (интерфейса) и телом новосозданного анонимного класса.
Так как анонимный класс является локальным классом, он имеет все те же ограничения, что и локальный класс.
Использование анонимных классов оправдано во многих случаях, в частности когда:
Ну вот наверное и все. Остается только сказать, что использование с умом возможностей, которые предоставляют вложенные классы, сделают ваш код чище, красивее и понятнее.
Константин c0nst Чапюк, 23.04.2009
Если Вам понравилась статья, проголосуйте за нее
Голосов: 180 Голосовать
21. Java — Вложенные и внутренние классы
В этой главе мы обсудим внутренние классы Java.
Содержание
Вложенные классы
В Java, подобно методам, переменные класса тоже могут иметь в качестве своего члена другой класс. В Java допускается написание класса внутри другого. Класс, написанный внутри, называется в Java вложенным классом, а класс, который содержит внутренний класс, называется внешним классом.
Синтаксис
Ниже приведен синтаксис для записи вложенного класса. Здесь класс Outer_Demo – это внешний класс, а класс Inner_Demo – вложенный класс.
Вложенные классы в Java делятся на два типа:
Внутренние классы (нестатические вложенные классы)
Внутренние классы — это механизм безопасности в Java. Мы уже знаем, что класс не может быть связан с модификатором доступа private, но если у нас есть класс как член другого класса, то внутренний класс может быть private. И это также используется для доступа к закрытым (private) членам класса.
В Java внутренние классы имеют три типа в зависимости от того, как и где Вы их определяете:
Внутренние классы
Создать внутренний класс в Java довольно просто. Вам просто нужно написать класс внутри класса. В отличие от класса, внутренний класс может быть закрытым (private), и после того, как Вы объявляете внутренний класс закрытым, он не может быть доступен из объекта вне класса.
Ниже приведен пример создания внутреннего класса и получения доступа к нему. В данном примере мы делаем внутренний класс private и получаем доступ к классу с помощью метода.
Пример
Здесь Вы можете заметить, что Outer_Demo – внешний класс, Inner_Demo – внутренний класс, display_Inner() – метод, внутри которого мы создаем внутренний класс, и этот метод вызывается из основного метода.
Если Вы скомпилируете и выполните вышеуказанную программу, Вы получите следующий результат:
Доступ к частным (private) членам
Как упоминалось ранее, внутренние классы также используются в Java для доступа к закрытым членам класса. Предположим, у класса есть private члены. Для доступа к ним напишите в нем внутренний класс, верните частные члены из метода внутри внутреннего класса, скажем, методом getValue() и, наконец, из другого класса (из которого Вы хотите получить доступ к закрытым членам) вызовите метод getValue() внутреннего класса.
Чтобы создать экземпляр внутреннего класса, сначала Вам необходимо создать экземпляр внешнего класса. После этого, используя объект внешнего класса, Вы можете создать экземпляр внутреннего класса.
Следующий пример показывает, как получить доступ к закрытым членам класса с использованием внутреннего класса.
Пример
Если Вы скомпилируете и выполняете вышеуказанную программу, то получите следующий результат:
Локальный метод внутреннего класса
В Java мы можем написать класс внутри метода, и это будет локальный тип. Как и локальные переменные, возможности внутреннего класса ограничены в рамках метода.
Локальный метод внутреннего класса может быть создан только внутри метода, где определяется внутренний класс. Следующая программа показывает, как использовать локальный внутренний метод.
Пример
Получим следующий результат:
Анонимные внутренние классы в Java
Анонимный внутренний класс — это внутренний класс, объявленный без имени класса. В случае анонимных внутренних классов в Java мы объявляем и создаем их в одно и то же время. Как правило, они используются всякий раз, когда Вам необходимо переопределить метод класса или интерфейса. Синтаксис анонимного внутреннего класса в Java выглядит следующим образом:
Синтаксис
Пример
Следующая программа показывает, как переопределить метод класса с использованием анонимного внутреннего класса.
Получим следующий результат:
Точно так же Вы можете переопределить методы конкретного класса, а также интерфейс, используя в Java анонимный внутренний класс.
Анонимный внутренний класс как аргумент
Как правило, если метод принимает объект интерфейса, абстрактный класс или конкретный класс, то мы можем реализовать интерфейс, расширить абстрактный класс и передать объект методу. Если это класс, мы можем напрямую передать его методу.
Но во всех трех случаях Вы можете в Java передать анонимный внутренний класс методу. Синтаксис передачи анонимного внутреннего класса в качестве аргумента метода:
Синтаксис
Пример
Следующая программа показывает, как передать анонимный внутренний класс в качестве аргумента метода.
Если Вы скомпилируете и выполните вышеуказанную программу, то получите следующий результат:
Статический вложенный класс в Java
Статический внутренний класс — это вложенный класс, который является статическим членом внешнего класса. Доступ к нему возможен без создания экземпляра внешнего класса с использованием других статических элементов. Как и статические члены, статический вложенный класс не имеет доступа к переменным экземпляра и методам внешнего класса. Синтаксис статического вложенного класса в Java выглядит следующим образом:
Синтаксис
Пример
Создание экземпляра статического вложенного класса немного отличается от экземпляра внутреннего класса. Следующая программа показывает, как использовать статические вложенные классы.
Внутренние и вложенные классы java. Часть 2
Внутренние и вложенные классы java. Часть 2
02.03.2017 — 2019 год
Inner Classes — Внутренние классы
Внутренний класс связан с экземпляром его обрамляющего класса (из документации).
Пример внутреннего класса есть в документации.
Так в чем же отличие, спросите вы. Объявления классов и вложенных и внутренних
одинаковые в данных случаях. Отличие в том, что внутренний класс связан с внешним классом через экземпляр, или через объект класса.
Чтобы создать экземпляр внутреннего класса, нам нужно сначала создать экземпляр внешнего класса. Затем создать внутренний объект, в пределах внешнего объекта, таким образом:
По-другому мы можем написать так:
Рассмотрим свойства внутренних классов.
Внутренние классы есть смысл использовать, если они будут использовать элементы родителя,
чтобы не передавать лишнего в конструкторах. Внутренний класс неявно наследуется от внешнего класса, хотя мы не используем ключевое слово extends в случае с классом или implements в случае с интерфейсом. То есть, во внутреннем классе мы можем использовать весь унаследованный функционал внешнего класса. Может показаться, что это сомнительно. Но это дает нам более гибкий подход. Таким образом мы можем использовать во внутреннем классе, функционал унаследованный от внешнего класса.
Внутренний класс стоит использовать, когда нам нужна инкапсуляция. Во внутреннем классе мы, таким образом закрываем всё от «внешнего мира».
Например, Map.Entry — нигде кроме интерфейса Map и его реализаций он не используется. Смотрите исходный код Map.Entry и Map. Это я привел только один пример.
Далее рассмотрим пример явного наследования.
В этом примере у нас, по сути, получилось множественное наследование, и мы можем использовать функционал класса AnyClass и функционал класса Outer6.
рис. 1
Здесь модификатор доступа у класса Outer6 по умолчанию. То есть класс Outer6 виден только в нашем пакете (package inner). Класс Inner6 закрыт от внешнего мира и внешнего воздействия.
То есть более «защищен».
Это только пример множественного наследования от «прилегающего» класса и класса «оболочки». На практике вам вряд-ли такое понадобится. Тут я рассматриваю такую возможность только в учебных целях и для лучшего понимания.
Замечание Выражение: «прилегающего» класса — взято из книги «Философия Java».
В этом примере видно, что мы можем использовать как поля и методы «окружающего» класса — Outer7, так поля и методы того класса, от которого мы наследовали внутренний класс — AnyClass2. Это дает нам несколько большие возможности и гибкость при использовании внутреннего класса. Хотя для множественного наследования более подходят интерфейсы.
Совет из книги «Философия Java. Брюс Эккель. ISBN 5-272-00250-4» c. 313:
«Каждый внутренний класс может независимо наследовать определенную реализацию.
Внутренний класс не ограничен при наследовании в ситуациях, где внешний класс уже наследует реализацию.»
Чтобы использовать внутренний класс, за пределами обычных методов «окружающего» класса необходимо создать объект внутреннего класса следующим способом.
ИмяВнешнегоКласса.ИмяВнутреннегоКласса.
Объект внутреннего класса сохраняет информацию о месте, где он был создан.
Майкл Морган. «Java 2.Руководство разработчика» ISBN 5-8459-0046-8
Брюс Эккель. «Философия Java.» ISBN 5-272-00250-4
Герберт Шилдт «Java. Полное руководство. 8-е издание.» ISBN: 978-5-8459-1759-1
Внутренние и вложенные классы java. Часть 3
Внутренние и вложенные классы java. Часть 3
Локальные классы
Цитата из книги Effective Java TM. Programming Language Guide. Joshua Bloch. Издательство «Лори»:
«Локальный класс можно декларировать везде, где разрешается декларировать локальную переменную, и он подчиняется тем же самым правилам видимости. Локальный класс имеет несколько признаков, объединяющих его с каждой из трех других категорий вложенных классов. Как и классы-члены, локальные классы имеют имена и могут использоваться многократно. Как и анонимные классы, они имеют окружающий их экземпляр тогда и только тогда, когда применяются в нестатическом контексте. Как и анонимные классы, они должны быть достаточно короткими, чтобы не мешать удобству чтения метода или инициализатора, в котором они содержатся»
Выводы:
1. Локальные классы определяются в блоке кода и область их видимости — этот блок кода. Объекты локального класса могут создаваться в блоке кода, котором они описаны.
2. Локальные классы не имеют модификаторов доступа private или public, потому что они принадлежат не классу, а тому блоку кода, в котором они описаны.
3. Локальные классы, объявленные в статичном блоке, могут обращаться только к статичным полям внешнего класса.
4. Локальные классы могут иметь модификаторы доступа final
5. Локальные классы не могут быть статичными, за исключением static final.
6. В java8 мы можем обращаться из локального класса не только к финальным переменным внешнего класса, и к не финальным методам внешнего класса, если они не были изменены до момента инициализации класса.
7. Локальные классы имеют доступ к закрытым переменным внешнего класса, в контексте экземпляра.
То есть локальные классы ведут себя в этом отношении как внутренние классы.
Анонимные (безымянные) классы
Анонимным классом называется класс, в котором программист не задает явно имя экземпляру.
Простой пример:
В анонимном классе явно не задается конструктор класса, а используется конструктор базового класса.
Мы можем создать анонимный класс, передав некоторое значение в конструктор базового класса.
Случаи применения
Анонимные классы применяются там, где вам необходим функционал анонимного класса без повторного использования. При повторном использовании есть смысл создать отдельный класс. Добавить новый функционал анонимному классу вы можете так:
Здесь точка с запятой, в отличии от языка с++ означает не только окончание класса, а окончание блока кода.
Давайте немного более расширим пример:
Но в таком виде наш пример не будет работать. Ведь у анонимного класса нет конструктора, кроме как конструктора базового класса. Как же нам быть? Как запустить наш метод, если у нас нет имени класса, нет конструктора класса, а есть только метод?
У нас есть возможность выполнить наш метод из блока инициализации.
Дописываем блок инициализации и выполняем наш метод:
Напишу еще один учебный пример с пояснениями:
Анонимными классами не следует злоупотреблять, иначе у вас получится код, в котором будет сложно разобраться и поддерживать. Анонимные (внутренние, вложенные, локальные) применяются для сокрытия реализации.
Какое применение находят анонимные классы?
В основном для создания «слушателей» какого-то события.
Пример:
Здесь мы добавили к item новый «слушатель» события и далее переопределяем его метод под наши требования. Фактически мы используем анонимный класс new ActionListener() и переопределяем методы родительского класса ActionListener. Если нам понадобится, то мы можем получить имя анонимного класса. Развитие идеи анонимного класса с одним методом получило в виде лямбд в java8. Сама запись new ActionListener(); означает что мы создаем анонимный класс без имени унаследованный от ActionListener. Добавление функциональности нашему классу new ActionListener()*здесь>;
В этом примере мы получим имя анонимного класса:
Вызов метода анонимного класса:
Анонимный вложенный класс не является членом содержащего его класса. Анонимный класс определяется и порождает экземпляр в момент использования. Он не определяется вместе с остальными членами включающего его класса. Анонимный класс можно поместить в любом месте программы, где разрешается применять локальные переменные.
В зависимости от контекста анонимный класс, член другого класса, ведет себя по-разному.
В нестатическом контексте появляется окружающий его экземпляр, в статическом контексте ведет себя как статический.
Цитата из книги «Effective Java TM. Programming Language Guide. Joshua Bloch». Издательство «Лори»:
Существуют четыре категории вложенных классов, каждая из которых занимает свое место. Если вложенный класс должен быть виден за пределами одного метода или он слишком длинный для того, чтобы его можно было удобно разместить в границах метода, используйте класс-член. Если каждому экземпляру класса-члена необходима ссылка на включающий его экземпляр, делайте его нестатическим, в остальных случаях он должен быть статическим. Предположим, что класс находится внутри метода. Если вам нужно создавать экземпляры этого класса только в одном месте программы и уже есть тип, который характеризует это класс, сделайте его анонимным классом. В противном случае, это должен быть локальный класс.
Анонимный класс может быть не только наследуемым от другого класса(как правило, абстрактного), но и имплементировать интерфейс.
Широкое применение анонимные классы находят в создании многопоточности на java,
а также для создания «слушателей» события и «адаптеров».
Реальный пример использования анонимных классов на основе адаптеров:
На этом позвольте закончить. Надеюсь я написал довольно ясно для понимания и кратко.
А также написал и протестировал достаточно ясный код.
Мы с вами рассмотрели, что такое вложенные классы, внутренние классы, локальные и анонимные классы. Написали и протестировали код. Выяснили их свойства, некоторые области применения. Надеюсь, данная статья окажется полезной, и она найдет применение в вашей дальнейшей учебе и работе. Пишите отзывы, пожелания, комментарии, вопросы.
Майкл Морган. «Java 2. Руководство разработчика» ISBN 5-8459-0046-8
Брюс Эккель. «Философия Java.» ISBN 5-272-00250-4
Герберт Шилдт «Java. Полное руководство. 8-е издание.» ISBN: 978-5-8459-1759-1