Как мне исправить эти двойные скобки

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

private SelectItem notifyTypeItem = new SelectItem();

notifyTypeItem.setTitle("Default Notification");
    notifyTypeItem.setWidth("100%");
    notifyTypeItem.setValueMap(new LinkedHashMap<String, String>() {{
                   put("0", "None");
                   put("1", "Subtle");
                   put("2", "Intrusive");
               }}
    );

1 ответ

Решение

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

  1. Анонимные подклассы
  2. Блоки инициализатора экземпляра

TL;DR о том, как это исправить, это просто разделить эти put вызовы и инициализация из установщика:

Map<String, String> valueMap = new LinkedHashMap<String, String>();
valueMap.put("0", "None");
valueMap.put("1", "Subtle");
valueMap.put("2", "Intrusive");
notifyTypeItem.setValueMap(valueMap);

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

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

Анонимный класс - это, как правило, просто класс без имени. Например, вы можете создавать анонимные экземпляры интерфейса, такие как Runnable:

Runnable r = new Runnable() {
    @Override
    public void run() {
        // Do something
    }
};
r.run(); // Does that something

Точно так же вы можете создавать анонимные экземпляры абстрактных и конкретных классов тоже. Например, очень часто создается анонимный экземпляр ThreadLocal:

private static ThreadLocal<SimpleDateFormat> localIsoDateFormat = new ThreadLocal<SimpleDateFormat>() {

    @Override
    protected SimpleDateFormat initialValue() {
        return new SimpleDateFormat("yyyy-MM-dd");
    }
}

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

Блоки инициализатора экземпляра

Блок инициализатора экземпляра позволяет вам выполнять инициализаторы вне вашего конструктора. Например:

public class MyClass {

    private final String s;
    {
        s = "My Class String";
    }

    public String getS() { return s; }
}

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

Объединяя их

Ваш пример объединяет их. Это создает анонимный подкласс LinkedHashMapзатем он также использует блок инициализатора. Более правильно отформатирован ваш код:

Map<String, String> map = new LinkedHashMap<>() {
    {
        put("0", "None");
        put("1", "Subtle");
        put("2", "Intrusive");
    }
};

Это анонимный случай LinkedHashMap с блоком инициализатора экземпляра, который делает put звонки.

Почему это плохо?

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

Анонимные классы известны тем, что являются источником утечек памяти в вашем приложении. Ваш код, кажется, находится вstatic контекст. Это означает, что аноним LinkedHashMap созданный вами подкласс будет иметь неявную ссылку на класс, в котором находится ваш метод. Например, если ваш метод находится в MyClass:

public class MyClass {
    private SelectItem notifyTypeItem = new SelectItem();

    public void foo() {
        notifyTypeItem.setTitle("Default Notification");
        notifyTypeItem.setWidth("100%");
        notifyTypeItem.setValueMap(new LinkedHashMap<String, String>() {{
                   put("0", "None");
                   put("1", "Subtle");
                   put("2", "Intrusive");
               }}
        );
    }
}

Недавно созданный LinkedHashMap подкласс (MyClass$1 будет "имя класса", если вы можете назвать это так) будет иметь ссылку на вложение MyClass пример. В некоторых случаях это может быть хорошо. Но если вы создаете notifyTypeItem с намерением передать его чему-то другому и выбросить MyClass Например, вы будете создавать утечку памяти в вашем приложении. MyClass экземпляр будет ссылаться на MyClass$1 экземпляр, а SelectItem будет ссылаться на MyClass$1 Например, MyClass экземпляр никогда не будет собирать мусор, пока SelectItem На экземпляр больше не ссылаются. Если MyClass имеет несколько других ссылок, кроме SelectItem, то это только увеличит общий объем памяти одного MyClass Экземпляр потребляет, и привести к большим проблемам утечки памяти.


Рекомендации

Вот некоторые соответствующие ссылки:

Другие вопросы по тегам