Во что компилируется анонимный внутренний класс
Перейти к содержимому

Во что компилируется анонимный внутренний класс

  • автор:

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

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

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

Для лучшего изучения материала у вас должна быть некоторая подготовка.
То есть вам нужно знать: синтаксис языка java, область видимости переменных, классы, статические и нестатические члены класса, создание экземпляров класса, наследование, модификаторы доступа.

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

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

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

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

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

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

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

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

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

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

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

Выше я не случайно упомянул наследование и композицию. Это напрямую относится к дальнейшему материалу.

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

Если у вас есть возможность, вам пригодилось, и вы можете помочь, то нажмите кнопку поддержать автора материально.

Pro Java

Все классы, которые мы рассматривали до сих пор являлись классами верхнего уровня (то есть они являлись непосредственными членами пакетов и не были вложены в другие классы). В интерфейсах, мы кратко рассмотрели тему вложенных интерфейсов. В Java есть четыре типа классов, именуемых внутренними или вложенными классами (inner or nested classes), которые могут быть определены в Java программе.

Примечание: Программисты, пишущие на Java, не пришли к единому мнению по поводу именования различных типов внутренних классов. Поэтому вы можете обнаружить, что в разных ситуациях их называют поразному. Например статические внутренние классы иногда называют вложенными – nested, а простые внутренние классы – inner. По крайней мере так они определяются в оригинальной документации, а так же многих книгах и руководствах.

Terminology: Nested classes are divided into two categories: static and non-static. Nested classes that are declared static are called static nested classes. Non-static nested classes are called inner classes.

Как видите тут упомянуто две категории, на которые разбиваются четыре типа внутренних классов. По существу статические входят в одну категорию, а в другую три других типа внутренних классов.

Внутренние классы являются элегантным и мощным инструментом языка Java. Эти четыре типа классов представлены ниже:

Статические вложенные классы (static nested classes)

Статический вложенный класс – это класс (или интерфейс), определенный как static внутри другого класса. Метод с модификатором static называют методом класса, поэтому внутренний класс такого типа можно назвать «классом класса», но подобная терминология была бы очень запутанной. Статические вложенные классы ведут себя почти так же, как обычные классы верхнего уровня, за исключением того, что они могут получать доступ к static членам класса, в котором они содержатся. Интерфейсы так же могут быть определены как статические внутренние члены классов.

Внутренние классы (inner classes)

Внутренний класс определяется внутри окружающего класса, но он объявляется без модификатора static. Этот тип внутренних классов является аналогом методов и полей экземпляра. Экземпляр внутреннего класса всегда связан с экземпляром окружающего класса, а код внутреннего класса имеет доступ ко всем полям и методам (как статическим, так и нестатическим) окружающего класса. Есть несколько особенностей синтаксиса языка Java, проявляющихся при работе с экземпляром класса, содержащего внутренний класс. Интерфейсы могут быть определены только как статические члены, но не как нестатические.

Локальные классы (local classes)

Локальный класс – это класс, определенный в блоке Java-кода. Как и локальная переменная, локальный класс виден только внутри блока. Локальным классам присущи многие особенности внутренних классов. Кроме того, локальные классы могут работать с любыми final переменными или параметрами, которые доступны в блоке, где класс был определен. Интерфейсы нельзя определить локально.

Анонимные классы (anonymous classes)

Анонимный класс – это вид локального класса без имени; он комбинирует синтаксис для объявления класса с синтаксисом для присвоения значения объекту. Объявление локального класса – это оператор Java, а определение анонимного класса (и присвоение значения) – это выражение Java, поэтому такое определение может появляться только как часть еще большего выражения, например вызова метода. Интерфейсы нельзя определить анонимно.

Так же тут стоит упомянуть о том, что можно определить несколько классов верхнего уровня в одном файле .java. Такие классы не являются ни одним из типов внутренних классов. По существу это равнозначно что для каждого класса создается отдельный файл .java. Но если один .java файл содержит несколько классов верхнего уровня, только один из них может быть объявлен как public, остальные должны быть с пакетным доступом . И по имени класса public должен называться .java файл . Мы уже с этим встречались во множестве прошлых примеров кода, а тут я просто описал правило для этого случая. Обычно это используется для небольших тестовых или учебных программ, ну или для логической группировки связанных классов. Каждый класс верхнего уровня, который содержится в .java файле компилируется в отдельный .class файл с именем компилируемого класса . Приведу небольшой пример:

in01

Обратите внимание на подсветку скобок которые определяют границы класса Main. Класс Example не входит в эти границы и поэтому не является вложенным классом.

Если посмотреть на то файлы что сгенерировал компилятор, то мы увидим следующую картину:

Java Вложенные Классы — локальный класс, статический вложенный класс, анонимный внутренний класс

Вложенные классы в Java объявляются в теле другого класса. Вложенный класс может иметь модификатор доступа private, public, protected, тогда как внешний класс может иметь только public или default .

Вложенные классы делятся на два типа:

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

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

Статические объекты класса могут быть созданы с следующим образом:

Внутренний класс Java

Любой не-статический вложенный класс известен как внутренний класс. Внутренние классы связаны с объектом класса, и они могут получать доступ ко всем переменным и методам внешнего класса. Объект внутреннего класса являются частью объекта внешнего класса. Чтобы создать экземпляр внутреннего класса, мы должны сначала создать экземпляр внешнего класса.

Внутренние классы могут быть созданы так:

Есть два специальных вида внутренних классов Java.

Локальные внутренние классы

Если класс определяется в теле метода, то его называют локальным внутренним классом. В таких классах д опускаются только модификаторы abstract или final. Локальный внутренний класс может получить доступ ко всем членам внешнего класса и локальных final-переменных.

Локальный внутренний класс может быть определен так:

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

Локальный внутренний класс без имени известен как анонимный внутренний класс. Анонимный класс определяется и конкретизируется в одном заявлении. Анонимный внутренний класс всегда расширяет класс или реализовывает интерфейс. Так как анонимный класс не имеет имени, то не возможно определить конструктор для анонимного класса. Анонимные внутренние классы доступны только в точке, где она определена.

Это немного трудно понимать на слух, поэтому давайте посмотрим на примере.

Вот Java класс с внутренним классом, статическим вложенным классом, локальным внутренним классом и анонимным внутренним классом.

Уровень 24. Ответы на вопросы к собеседованию по теме уровня

Java-университет

Анонимные внутренние классы компилируются в файлы внешнийКласс$n.class . На месте внешнего класса, соответственно, название обрамляющего класса, внутри которого описывается анонимный внутренний класс. На месте n число от 1 до количества анонимных классов.

Можно ли наследовать внутренние классы?

Наследовать внутренние классы от других — можно.

Наследование от внутреннего класса получается чуть сложнее, чем обычное, так как конструктор внутреннего класса связывается со ссылкой на окружающий внешний объект. Проблема состоит в том, что «скрытая» ссылка на объект объемлющего внешнего класса должна быть инициализирована, а в производном классе больше не существует объемлющего объекта по умолчанию. Для явного указания объемлющего внешнего объекта применяется специальный синтаксис:

Здесь класс InheritInner расширяет только внутренний класс, а не внешний. Но когда дело доходит до создания конструктора, предлагаемый по умолчанию конструктор не подходит, и вы не можете просто передать ссылку на внешний объект. Необходимо включить в тело конструктора выражение ссылкаНаОбъемлющийКласс.super(); в теле конструктора. Оно обеспечит недостающую ссылку, и программа откомпилируется.

Можно ли наследовать анонимные внутренние классы?

Описывая анонимный класс мы уже наследуемся от какого-то класса или реализуем какой-либо интерфейс. К анонимным классам напрямую нельзя применить слова extends или implements, но ведь никто не мешает заранее подготовиться и расширить нужный интерфейс, который будем реализовывать с помощью анонимного класса. Пример в коде ниже.

Наследоваться от анонимного класса нельзя.

Можно ли переопределять внутренние классы?

Переопределение внутреннего класса, как если бы он был еще одним методом внешнего класса, фактически не имеет никакого эффекта:

Вывод:

Конструктор по умолчанию автоматически синтезируется компилятором, а в нем вызывается конструктор по умолчанию из базового класса. Можно подумать, что при создании объекта BigEgg должен использоваться «переопределенный» класс Yolk , но это отнюдь не так, как видно из результата работы программы.

Этот пример просто показывает, что при наследовании от внешнего класса ничего особенного с внутренними классами не происходит. Два внутренних класса — совершенно отдельные составляющие, с независимыми пространствами имен. Иными словами нельзя.

Какие ограничения есть у локальных классов?

Вначале вспомним что такое локальный класс. Это класс, описанный в блоке кода, то есть, по-простому — между кавычек <> . Наиболее часто эти кавычки являются телом метода. Но могут они быть и просто блоком, статическим блоком, телом if -ов, циклов и т.д.

Локальный класс наделён особенностями внутренних классов, но имеет отличительные черты, а именно:

  1. он имеет доступ только к финальным полям и аргументам обрамляющего метода, а также ко всем полям обрамляющего класса, в том числе приватным и статическим;
  2. локальный класс виден и может создаваться только в блоке, в котором описан;
  3. у локального класса не ставится модификатор доступа;
  4. не может иметь статических полей, методов, классов (за исключением финальных);
  5. локальный класс, объявленный в статическом блоке может обращаться только к статическим полям внешнего класса.

Но! Начиная с Java8 мы можем обращаться в локальных классах к не финальным локальным переменным, если они не были изменены до момента инициализации класса. Также теперь стало возможным обращение к не финальным параметрам метода.

Может ли анонимный внутренний класс содержать статические методы?

Нет. У Анонимных внутренних классов, как и у внутренних классов не может быть статических полей, методов. (вспомним, что анонимные классы компилируются в обычные внутренние, а те, в свою очередь, связаны с объектом обрамляющего класса)

Можно ли создать объект внутреннего класса, если у внешнего класса только private конструктор?

Имея подобный код:

Напрямую, в другом классе (вне обрамляющего), конечно, создать объект InnerClass следующим способом не получится:

Но! Что если у нас есть метод, возвращающий экземпляр

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

Можно ли объявлять внутренние классы private ?

PS Обоснования так и не нашел, но на философии java встречались подобные примеры. Плюс IDE не ругается. Буду признателен за обоснование, но предположу, что в этом плане внутренний класс ничем не отличается от обычного класса.

Можно ли объявлять анонимные внутренние классы private ?

Аналогично (в плане не нашел обоснования). Можно объявить private переменную от типа которой наследуется наш анонимный класс.

Сколько у класса максимально может быть внутренних классов?

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

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