Findbugs возражает против анонимного внутреннего класса

Этот код:

Set<Map.Entry<String, SSGSession>> theSet =  new TreeSet<Map.Entry<String, SSGSession>>(new Comparator<Map.Entry<String, SSGSession>>() {

        @Override
        public int compare(final Map.Entry<String, SSGSession> e1, final Map.Entry<String, SSGSession> e2) {
            return e2.getValue().getStartTime().compareTo(e1.getValue().getStartTime());
        }
    }));

вызывает нарушение в Sonar, отключая правило findbugs "SIC_INNER_SHOULD_BE_STATIC_ANON", в котором есть описание:

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

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

Я не возражаю против этого, так как наши строго соблюдаемые стандарты кодирования являются "нарушениями сонара со звуком", но я сильно склонен рассуждать об этом для //NOSONAR здесь, поскольку imho, извлекающий один метод линии для статического внутреннего делает код немного сложнее для grok.

Что думают кураторы java?

Ответ 1

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

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

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

Кроме того, я не уверен, что это сделать, поскольку статический класс будет менее понятным, даже если он добавит немного больше шаблонов (и извините, если ниже это не 100% правильный код, моя Java немного ржавая, не стесняйтесь предложите изменить):

Set<Map.Entry<String, SSGSession>> theSet 
    = new TreeSet<Map.Entry<String, SSGSession>>(new SSGSessionStartTimeComparator());

И затем где-нибудь еще в файле вместе с другими статическими классами:

static class SSGSessionStartTimeComparator extends Comparator<Map.Entry<String, SSGSession>>() {
    @Override
    public int compare(final Map.Entry<String, SSGSession> e1, final Map.Entry<String, SSGSession> e2) {
        return e2.getValue().getStartTime().compareTo(e1.getValue().getStartTime());
    }
}

Ответ 2

Просто для полноты, я хотел бы добавить еще один вариант к отличным ответам, уже предоставленным. Определите константу для Comparator и используйте это:

private static final Comparator<Map.Entry<String, SSGSession>> BY_STARTTIME =
        new Comparator<Map.Entry<String, SSGSession>>() {
    @Override
    public int compare(final Map.Entry<String, SSGSession> e1,
            final Map.Entry<String, SSGSession> e2) {
        return e2.getValue().getStartTime().compareTo(e1.getValue().getStartTime());
    }
};

private void foo() {
    Set<Map.Entry<String, SSGSession>> theSet =
        new TreeSet<Map.Entry<String, SSGSession>>(BY_STARTTIME);
}

Это сохранит дополнительный класс, как в hyde answer. В противном случае hyde answer лучше, потому что он позволяет объявить Comparator сериализуемым (что и есть, потому что у него нет состояния). Если Comparator не является сериализуемым, ваш TreeSet также не будет сериализуемым.

Ответ 3

Здесь есть три решения, лучшие из которых находятся вне вашего контроля:

  • Расширьте синтаксис Java:

    ... theSet = new static Comparator ...
    
  • Объявить и использовать статический класс, как описано.

  • Игнорировать предупреждение в этом одном экземпляре:

    @SuppressFBWarnings("SIC_INNER_SHOULD_BE_STATIC_ANON")
    ... your method ...
    

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

Ответ 4

Просто примечание: анонимные внутренние классы - отличный способ утечки памяти, особенно при использовании в JEE beans. Что-то просто: new HashMap<>() {{ put"("a","b"); }};

в bean, аннотированный с помощью @javax.ejb.Singleton может привести к тому, что несколько экземпляров будут сохранены в живых, поскольку эта карта сохраняет ссылку на bean.