Вопрос в том, сделать ли вложенный класс в Java статическим вложенным классом или внутренним вложенным классом. Я искал здесь и на Stack Overflow, но не смог найти никаких вопросов, касающихся последствий этого решения для дизайна.
Я нашел вопросы о разнице между статическими и внутренними вложенными классами, что мне ясно. Однако я еще не нашел убедительной причины использовать статический вложенный класс в Java - за исключением анонимных классов, которые я не рассматриваю для этого вопроса.
Вот мое понимание эффекта от использования статических вложенных классов:
- Меньше связывания: мы обычно получаем меньше связывания, так как класс не может напрямую получить доступ к атрибутам своего внешнего класса. Меньшее связывание обычно означает лучшее качество кода, более легкое тестирование, рефакторинг и т. Д.
- Одиночный
Class
: загрузчик классов не должен заботиться о новом классе каждый раз, когда мы создаем объект внешнего класса. Мы просто получаем новые объекты для одного и того же класса снова и снова.
Для внутреннего класса я обычно нахожу, что люди рассматривают доступ к атрибутам внешнего класса как профессионал. Я позволю себе расходиться в этом отношении с точки зрения дизайна, так как этот прямой доступ означает, что у нас есть высокая связь, и если мы когда-нибудь захотим извлечь вложенный класс в его отдельный класс верхнего уровня, мы можем сделать это только после существенного поворота это в статический вложенный класс.
Таким образом, мой вопрос сводится к следующему: я ошибаюсь, полагая, что доступ к атрибутам, доступный для нестатических внутренних классов, приводит к высокой связи, следовательно, к снижению качества кода, и что из этого я могу сделать вывод, что (неанонимные) вложенные классы должны вообще быть статичным?
Или другими словами: существует ли убедительная причина, по которой человек предпочитает вложенный внутренний класс?
Ответы:
Джошуа Блох в пункте 22 своей книги «Эффективное Java, второе издание» рассказывает, когда использовать, какой тип вложенного класса и почему. Ниже приведены некоторые цитаты:
Одним из распространенных применений статического класса-члена является открытый вспомогательный класс, полезный только в сочетании с его внешним классом. Например, рассмотрим перечисление, описывающее операции, поддерживаемые калькулятором. Перечисление Operation должно быть открытым классом статического члена
Calculator
класса. КлиентыCalculator
могут затем ссылаться на операции, используя такие имена, какCalculator.Operation.PLUS
иCalculator.Operation.MINUS
.Одним из распространенных применений нестатического класса-члена является определение адаптера, который позволяет рассматривать экземпляр внешнего класса как экземпляр некоторого несвязанного класса. Например, реализация
Map
интерфейса обычно использует нестатические классы членов реализовать свои взгляды сбора , которые возвращаются наMap
«SkeySet
,entrySet
иvalues
методе. Точно так же реализации интерфейсов коллекции, такие какSet
иList
, обычно используют нестатические классы-члены для реализации своих итераторов:Если вы объявляете класс-член, который не требует доступа к включающему экземпляру, всегда помещайте
static
модификатор в его объявление, делая его статическим, а не нестатическим классом-членом.источник
MySet
экземпляров внешнего класса ? Я мог бы представить что-то вроде зависимого от пути типа, являющегося разницей, то естьmyOneSet.iterator.getClass() != myOtherSet.iterator.getClass()
, но опять же, в Java это фактически невозможно, так как класс был бы одинаковым для каждого.Вы правы, предполагая, что доступ к атрибуту, доступный для нестатических внутренних классов, приводит к высокой связи, следовательно, к низкому качеству кода, и (неанонимные и нелокальные ) внутренние классы обычно должны быть статическими.
Конструктивные последствия решения сделать внутренний класс нестатичным изложены в Java Puzzlers , Puzzle 90 (жирный шрифт в приведенной ниже цитате - мой):
Если вам интересно, более подробный анализ головоломки 90 представлен в этом ответе в Stack Overflow .
Стоит отметить , что выше, по существу , является расширенной версией руководящих указаний в Java классов и объектов учебник :
Таким образом, ответ на вопрос , который вы просили в других словах в учебник, только убедива причина использовать Нестатический когда доступ к непубличным полем и методам объемлющего экземпляра является требуется .
Формулировка учебника несколько широка (это может быть причиной того, что Java Puzzlers пытается усилить и сузить ее). В частности, в моем опыте никогда не требовался непосредственный доступ к вмещающим полям экземпляров - в том смысле, что альтернативные способы, такие как передача их в качестве параметров конструктора / метода, всегда оказывались проще в отладке и обслуживании.
В целом, мои (довольно болезненные) встречи с отладочными внутренними классами, непосредственно обращающимися к полям включающего экземпляра, создали сильное впечатление, что эта практика напоминает использование глобального состояния вместе с известными пороками, связанными с ним.
Конечно, Java делает так, чтобы повреждение такого «квази-глобального» было заключено во включающем классе, но когда мне пришлось отлаживать определенный внутренний класс, я чувствовал, что такая помощь от группы не помогает уменьшить боль: у меня все еще было иметь в виду «чужую» семантику и детали, вместо того чтобы полностью сосредоточиться на анализе конкретного проблемного объекта.
Для полноты картины могут быть случаи, когда приведенные выше рассуждения не применяются. Например, согласно моему прочтению javadocs map.keySet , эта функция предполагает тесную связь и в результате делает недействительными аргументы против нестатических классов:
Не то чтобы вышесказанное каким-то образом сделало бы сложный код более легким в обслуживании, тестировании и отладке, но это, по крайней мере, позволило бы утверждать, что сложность соответствует / оправдана предполагаемой функциональностью.
источник
Некоторые заблуждения:
IIRC - На самом деле, это возможно. (Конечно, если они являются полями объекта, у него не будет внешнего объекта для просмотра. Тем не менее, если он получит такой объект из любого места, он сможет напрямую получить доступ к этим полям).
Я не совсем уверен, что вы имеете в виду здесь. Загрузчик классов задействуется только дважды, один раз для каждого класса (когда класс загружен).
Рамблинг следует:
В Javaland мы, кажется, немного боимся создавать классы. Я думаю, что это должно быть значимой концепцией. Как правило, принадлежит в своем собственном файле. Это нуждается в значительном шаблоне. Нужно обратить внимание на его дизайн.
По сравнению с другими языками - например, Scala или, может быть, C ++: абсолютно нет драмы, создающей крошечный держатель (класс, структура и т. Д.) Каждый раз, когда два бита данных принадлежат друг другу. Гибкие правила доступа помогают вам, сокращая шаблон, позволяя вам физически приблизить соответствующий код. Это уменьшает ваше «умственное расстояние» между двумя классами.
Мы можем отбросить проблемы дизайна в отношении прямого доступа, оставив внутренний класс закрытым.
(... Если бы вы спросили «почему нестатический публичный внутренний класс?», Это очень хороший вопрос - я не думаю, что я когда-либо нашел оправданное обоснование для таких случаев).
Вы можете использовать их в любое время, когда это помогает с удобочитаемостью кода и позволяет избежать нарушений и шаблонов. У меня нет готового примера, потому что в моем опыте такое случается не так часто, но это случается.
Статические внутренние классы все еще являются хорошим подходом по умолчанию , кстати. Нестатическое имеет дополнительное скрытое поле, сгенерированное, через которое внутренний класс ссылается на внешний.
источник
Может использоваться для создания фабричного шаблона. Заводской шаблон часто используется, когда созданным объектам для работы нужны дополнительные параметры конструктора, но каждый раз предоставлять их утомительно:
Рассмотреть возможность:
Используется как:
То же самое может быть достигнуто с помощью нестатического внутреннего класса:
Использовать как:
Фабрики выигрывают от того, что имеют специально названные методы, например
create
,createFromSQL
илиcreateFromTemplate
. То же самое можно сделать и здесь в виде нескольких внутренних классов:Требуется меньше передачи параметров от фабрики к сконструированному классу, но читаемость может немного пострадать.
Для сравнения:
против
источник