2010-02-17 5 views
20

В некоторых моих проектах и ​​в некоторых книгах говорилось, что не использовать внутренний класс (анонимный или нет, статический или нет) - за исключением некоторых ограниченных условий, например EventListener s или Runnable s - это наилучшая практика. Они даже были «запрещены» в моем первом промышленном проекте.Java (анонимные или нет) внутренние классы: хорошо ли их использовать?

Это действительно лучшая практика? Зачем?

(я должен сказать, что я их использую много ...)

- EDIT ---
я не могу выбрать правильный ответ во всех этих ответах: есть часть правоты на большинстве из них: я все равно буду использовать внутренние классы, но я постараюсь использовать их реже!

ответ

25

В мой взгляд, 90% внутренних классов в Java-коде являются либо сущностями, которые связаны с одним классом, и, таким образом, «заперты» в качестве внутренних классов или анонимными внутренними классами, которые существуют, потому что Java не поддерживает Lambdas.

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

Это оставляет меня с необходимыми внутренними классами - такими как слушатели действий, поддельные «функциональные» программирование и т. Д. Они часто являются анонимными и пока я не поклонник (предпочли бы Лямбду во многих случаях), я живу с ними, но им не нравятся.

Я не делал никаких C# в течение многих лет, но мне интересно, снижается ли распространенность внутренних классов или что-то вроде эквивалента C# при введении Lambdas.

+3

Для справки я должен упомянуть, что ** Java 8 ** вводит [** Lambda Expressions **] (https://docs.oracle.com/javase/tutorial/java/javaOO/lambdaexpressions.html). – vellotis

+1

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

16

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

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

+2

По моему опыту, как только вы делаете внутренний класс, вам все равно нужно это где-то в другом месте :) – Dolph

+12

Легче понять код, если он разбит на логические части, да, но в * моей * книге это означает лучше всего определить что-то как можно ближе к тому, где оно используется, вместо того, чтобы произвольно разбить его на собственный файл. ;) – Porculus

+2

Внутренние классы не исключают, что вы используете их вне класса, в котором они живут.Enums - хороший пример: Enums обычно используются для одного класса, делают его общедоступным, а затем у вас есть контекст того, где предполагается использовать Enum. Фабричные шаблоны являются хорошим местом для внутренних классов для создания реализаций, не загрязняя пространство имен пакетов теми вещами, которые должны быть созданы только в определенных случаях, контролируемых объектом Factory. –

6

Анонимные классы полезны при программировании на основе событий, особенно в качелях.

+1

Почему в программировании на основе событий, а не для других применений, например структур, используемых для перегруппировки многочисленных параметров, ... Это скорее догма, чем объяснение: -1. – Guillaume

1

Внутренние классы подходят при попытке эмулировать множественное наследование. Это похоже на то, что происходит под капотом с C++: когда у вас есть множественное наследование в C++, макет объекта в памяти на самом деле является конкатенацией нескольких экземпляров объектов; компилятор затем определяет, как «этот» указатель должен быть отрегулирован при вызове метода. В Java нет множественного наследования, но внутренний класс может использоваться для предоставления «представления» данного экземпляра под другим типом.

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

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

(Незначительный недостаток внутренних классов является то, что они добавляют один дополнительный уровень отступов в исходном коде. Это немного утомительно время от времени, когда один хочет, чтобы сохранить код в 79 столбцов.)

1

Анонимные внутренние классы часто используются, когда нам нужно реализовать интерфейс с помощью одного метода, например Runnable, ActionListener и некоторых других.

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

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

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

0

Да, это полезно использовать, когда вы пытаетесь сохранить связность класса, а классы никогда не должны создаваться вне их контекста внешнего класса, сделать конструкторы частными, и у вас действительно хорошая сплоченная инкапсуляция. Любой, кто говорит, что вы должны НИКОГДА использовать их не знает, о чем они говорят. Для обработчиков событий и других вещей, которые превосходят анонимные внутренние классы, они лучше, чем альтернатива загромождения пространства имен пакетов множеством обработчиков событий, которые применяются только к определенному классу.

1

Внутренние классы часто используются для «передачи поведения» в качестве параметра метода. Эта возможность поддерживается элегантным способом на других языках с закрытием. Использование внутренних классов создает не очень элегантный код (IMHO) из-за ограничения языка, но он полезен и широко используется для обработки событий и blocks в целом с внутренними классами.

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

3

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

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

4

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

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

+1

По крайней мере, это заставляет меня улыбаться, и это хорошее использование когда-то глупых правил, найденных в политике кодирования компании! – Guillaume

+0

Ничего особенного в обработчиках событий, основная цель вложенных классов - инкапсуляция. – bharatj

2

Определенные рамки, такие как Wicket, действительно требуют анонимных внутренних классов.

Говорить никогда не бывает глупо. Никогда не говори никогда!Примером хорошего использования может быть ситуация, когда у вас есть какой-то унаследованный код, написанный кем-то, где многие классы действуют непосредственно в поле «Коллекция», и по какой-либо причине вы не можете изменить эти другие классы, но вам необходимо условно зеркально отразить операции с другим Коллекция. Самое простое - добавить это поведение через анонимный внутренний класс.

bagOfStuff = new HashSet(){ 
    @Override 
    public boolean add(Object o) { 
    boolean returnValue = super.add(o); 
    if(returnValue && o instanceof Job) 
    { 
     Job job = ((Job)o); 
     if(job.fooBar()) 
     otherBagOfStuff.add(job); 
    } 
    return returnValue; 
    } 
} 

Таким образом, они могут быть использованы как закрытые люди.

2

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

Таким образом, вы можете легко скопировать код внутреннего класса во многих местах ... Это не проблема, когда вы используете очень простые внутренние классы для фильтрации/сортировки коллекций, используя предикаты, компараторы или что-то в этом роде.

Но вы должны знать, что, когда вы используете 3 раза анонимный внутренний класс, который делает точно так же (например, удаление «коллекции»), вы фактически создаете 3 новых класса на java PermGen.

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

Обратите внимание, что это также является причиной того, что двойной кудрявый синтаксис скобки (анонимный InnerClass с нестатическим блоком инициализации) иногда рассматриваются как антипаттерны:

new ArrayList<String>() {{ 
    add("java"); 
    add("jsp"); 
    add("servlets"); 
    }} 

вы должны спросить человек, которые запрещают использовать их ... ИМХО все зависит от контекста .. .

+0

Это хороший момент против внутреннего класса. По крайней мере, когда вы объявляете какое-то «стандартное» поведение. – Guillaume

+0

Это неистовое использование внутренних классов. Почему бы просто не создать список массивов и не добавить строки? Показ глупого примера внутренних классов не делает их использование плохой практикой. – ncmathsadist

+0

@ncmathsadist Я не говорю, что это хорошо, но некоторые люди считают его более читаемым и используют его в модульных тестах, где перджен не такой большой. –

3

Я предлагаю быть осторожным при его использовании, если ему нужен параметр метода. Я просто нашел утечку памяти, связанную с этим. Он включает HttpServlet с использованием GrizzlyContinuation.
Короче вот код ошибки:

public void doGet(HttpServletRequest request, final HttpServletResponse response){ 
    createSubscription(..., new SubscriptionListener(){ 
    public void subscriptionCreated(final CallController controller) { 
     response.setStatus(200); 
     ... 
     controller.resume(); 
    } 

    public void subscriptionFailed(){ 
     ... 
    } 

    public void subscriptionTimeout(){ 
     ... 
    }}); 
} 

Так, так как слушатель хранится у абонемента HttpServletResponse также хранится в том случае, слушатель должен его (не очевидно). Затем экземпляр HttpServletResponse будет выпущен, только если подписка будет удалена. Если вы используете внутренний класс, который получает ответ в его конструкторе, он может быть установлен равным null после того, как вызов возобновил освобождение памяти.

Используйте их, но будьте осторожны!

Martin

2

Один элемент, который не упоминается здесь в том, что (не статический) внутренний класс несет ссылку на это ограждающий класс. Что еще более важно, внутренний класс имеет доступ к закрытым членам его закрывающего класса. Это может потенциально разрушить инкапсуляцию.

Не используйте внутренний класс, если у вас есть опция.

+0

Статический внутренний класс? Do not Если он подчинен другому классу, сделайте его непубличным классом в том же файле. Неспособность использовать внутренние классы для слушателей означает сохранение болезненных цепочек геттеров и взаимное связывание переменных состояния, которые являются всего лишь уродливым хешем. – ncmathsadist

Смежные вопросы