Вложенные классы

Вложенные классы

Дорогу осилит идущий

Как мы знаем, обычно класс(интерфейс, енам) объявляется в отдельном файле. Можно, но некорректно, объявить и несколько независимых классов в одном файле, хоть это и плохая практика, имеющая свои ограничения (public-класс в файле может быть только один).

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

Предлагаю познакомиться со статьей: https://metanit.com/java/tutorial/3.12.php

а потом разобраться с ключевыми моментами и особенностями использования.

Также предлагаю посмотреть видео:


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

Итак, какие же особенности имеют inner-классы:

  1. Объект inner-класса всегда привязан к объекту родительского класса;
  2. Исходя из п.1, в inner-классе гарантированно можно получить доступ к this внешнего класса. Выглядит это как <ВнешнийКласс>.this. Например, ExternalClass.this;
  3. Исходя из п.1, объект внутреннего класса не может быть создан без объекта внешнего. Поэтому вызов конструктора внутреннего класса может выглядеть так: new ExternalClass().new InternalClass(). Разумеется, код будет выглядеть красивее, если объект внешнего класса был создан заранее и записан в переменную;
  4. Исходя из п.2, внутренний класс имеет доступ ко всем private полям, методам и конструкторам внешнего класса;
  5. Поскольку внутренний класс всегда хранит ссылку на объект внешнего, удаление объекта внешнего класса невозможно, пока есть активные ссылки на объект внутреннего. Это один из классических примеров утечки памяти (memory leak) в Java.

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

Достаточно распространенный пример внутреннего класса — класс «Колесо» внутри класса «Машина».

На практике внутренние классы не используются. Слишком много синтаксических ограничений, потенциальные проблемы с утечками памяти, отсутствие реальной выгоды в сравнением с использованием двух самостоятельных классов, отношения которых будут реализованы с помощью Java-кода, а не языковых ограничений — все эти факторы действуют не в пользу внутренних классов.


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

С синтаксисом нас познакомила статья на метанит. Попробуем разобраться с особенностями статических классов:

  1. Как и любой static, статический вложенный класс относится к классу (внешнему), а не объекту. Поэтому доступ к this внешнего класса отсутствует;
  2. Зато вложенный класс имеет доступ ко всем static полям и методам внешнего класса, включая приватные;
  3. Также статическому классу доступны приватные конструкторы внешнего класса, а при создании объекта — его приватные поля и методы;
  4. В отличии от внутреннего класса, создание объектов статического не требует создания объектов внешнего.
  5. Создание объекта статического класса выглядит примерно так: new ExternalClass.StaticClass(). Т.е. после ключевого слова new мы указываем название внешнего класса, а после точки — вызываем конструктор статического класса.

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

Если ваш публичный метод возвращает (или принимает параметром) объект статического класса (впрочем, как и внутреннего) — вы допустили ошибку в проектировании.


Анонимные классы

К сожалению, подходящей статьи у меня нет, поэтому с синтаксисом познакомимся через видео:

Как видите, анонимный класс имеет две особенности:

  1. У него нет названия, что делает проблематичным его переиспользование;
  2. Он всегда наследует другой класс (или реализует интерфейс).

В рамках анонимного класса вы, также, можете определять собственные поля и методы, в т.ч. статические. Статическая функциональность не будет иметь смысла, но синтаксически не существует ограничения на ее объявление. Приватные методы, технически, могут иметь смысл, но их использование — плохая практика, поскольку обычно анонимные классы определяются внутри метода, а раздувать методы не рекомендуется. В целом, стоит делать анонимные классы максимально лаконичными.

На самом деле, анонимные классы - самые популярные из всех видов вложенных классов. С приходом Java 8 их стали вытеснять лямбда-выражения (все тот же анонимный класс, но с ограничениями и синтаксическим сахаром, мы с ними познакомимся позже и будем использовать достаточно часто) и, в целом, функциональный подход к разработке. Но анонимные классы все еще часто можно встретить в старых приложениях и в android-разработке на Java. Там, например, анонимные классы часто используют для описания обработчиков действия пользователя (логика, работающая при нажатии на кнопку, например, почти гарантированно будет описана с использованием анонимного класса).

На практике анонимные классы создают в случаях, когда нужно переопределить несколько методов, не создавая полноценный класс. Т.е. это почти всегда про создание анонимных наследников абстрактных классов или интерфейсов. Но синтаксически мы можем создать анонимный класс на основе любого класса, включая Object (new Object() {...}). Другой вопрос, будет ли это иметь какой-либо смысл.


Локальные классы

Об этой механике скажем буквально несколько предложений — скорее всего, вы ее никогда не увидите, в силу ее бесполезности.

Тем не менее, в Java можно создать класс внутри метода. Областью видимости такого класса будет тело(!) метода (как параметр или возвращаемый тип он будет недоступен), в котором он описан.

Пример синтаксиса:

public void doSth() {
  //логика метода

  class LocalClass {
    //поля, методы и конструкторы
  }

  //логика метода
}

Внутри локального класса нам доступно все то же самое, что и в обычном классе. Но сам локальные класс должен быть объявлен без модификаторов доступа, иначе — ошибка компиляции.

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

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


С теорией на сегодня все!


Переходим к практике:

Задача 1:

Реализуйте задачу https://github.com/KFalcon2022/practical-tasks/tree/master/src/com/walking/lesson16_abstract_class_interface/task1_interface

используя

Вариант 1: анонимные классы;

Вариант 2: внутренние классы;

Вариант 3; статические вложенные классы.


Задача 2:

Реализуйте задачу https://github.com/KFalcon2022/practical-tasks/tree/master/src/com/walking/lesson14_polymorphism/task2

используя

Вариант 1: анонимные классы;

Вариант 2: внутренние классы;

Вариант 3; статические вложенные классы.


Если что-то непонятно или не получается – welcome в комменты к посту или в лс:)

Канал: https://t.me/+relA0-qlUYAxZjI6

Мой тг: https://t.me/ironicMotherfucker

 

Дорогу осилит идущий!

Report Page