Как мне исправить эти двойные скобки
Здравствуйте, я пытаюсь привести в порядок некоторый код, и я наткнулся на некоторые двойные скобки. Я понимаю, что это грех, и что я должен это исправить. Тем не менее, я понятия не имею, с чего начать. Кто-нибудь может помочь?
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 ответ
Чтобы понять, как это исправить, вы должны сначала понять, что он делает. Для этого вам нужно знать о двух вещах:
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
Экземпляр потребляет, и привести к большим проблемам утечки памяти.
Рекомендации
Вот некоторые соответствующие ссылки:
- Трейл анонимных классов Java (трейл Java, docs.oracle.com)
- Инициализация полей Java Trail (Java trail, docs.oracle.com)
- Что такое инициализация двойной скобки в Java? (ТАК вопрос, stackru.com)
- Почему не стоит использовать инициализатор с двойными скобками (запись в блоге Ниша Тахира, blog.nishtahir.com)
- Когда именно утечка безопасна для использования (анонимных) внутренних классов? (ТАК вопрос, stackru.com)