Внутренние классы Java (анонимные или нет): хорошо ли их использовать? - PullRequest
20 голосов
/ 18 февраля 2010

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

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

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

- РЕДАКТИРОВАТЬ ---
Я не могу выбрать правильный ответ во всех этих ответах: в большинстве из них есть доля правильности: я все еще буду использовать внутренние классы, но я постараюсь использовать их реже!

Ответы [ 15 ]

26 голосов
/ 18 февраля 2010

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

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

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

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

17 голосов
/ 18 февраля 2010

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

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

6 голосов
/ 18 февраля 2010

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

4 голосов
/ 16 августа 2011

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

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

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

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

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

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

Вам следует обратиться к людям, которые запрещают вам использовать их ... ИМХО, все зависит отконтекст ...

4 голосов
/ 18 февраля 2010

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

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

3 голосов
/ 01 декабря 2011

Я предлагаю быть осторожным при его использовании, если для этого требуется параметр метода. Я только что обнаружил утечку памяти, связанную с этим. Он включает 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 будет освобожден, только если подписка удалена. Если вы используете внутренний класс, который получает ответ в своем конструкторе, его можно установить равным нулю после возобновления вызова, освобождая память.

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

Martin

3 голосов
/ 18 февраля 2010

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

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

2 голосов
/ 18 января 2012

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

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

2 голосов
/ 01 ноября 2010

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

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

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;
  }
}

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

1 голос
/ 16 марта 2018

Код без внутренних классов больше поддерживаемый и читаемый . Когда вы обращаетесь к закрытым членам данных внешнего класса из внутреннего класса, JDK-компилятор создает package-access функций-членов во внешнем классе для внутреннего класса для доступа к private members. Это оставляет дыру в безопасности . В Вообще, мы должны избегать использования внутренних классов.

Использовать внутренний класс только тогда, когда внутренний класс имеет значение только в контекст внешнего класса и / или внутреннего класса может быть закрытым, так что только внешний класс может получить к нему доступ. Внутренние классы используются главным образом для реализации вспомогательных классов, таких как Iterators, Comparators и т. Д. , которые используются в контекст внешнего класса.

...