Podwójne nawiasy klamrowe jako antywzorzec podczas inicjalizacji obiektów
Podwójne nawiasy klamrowe jako antywzorzec podczas inicjalizacji obiektów
Na początek od razu przykład kodu gdzie inicjalizacja obiektu odbywa się z użyciem podwójnych nawiasów klamrowych:
public class Helper { public Map doubleCurlyBraces() { Map source = new HashMap() {{ put("firstName", "John"); put("lastName", "Smith"); put("organizations", new HashMap() {{ put("0", new HashMap() {{ put("id", "1234"); }}); put("abc", new HashMap() {{ put("id", "5678"); }}); }}); }}; } }
Powyższa konstrukcja wygląda na pierwszy rzut oka co najmniej dziwnie. Przyjrzyjmy się teraz temu bardziej szczegółowo:
- zapis w którym używamy klasy anonimowej:
new HashMap() { }
- w definicji klasy anonimowej jest z kolei blok inicjalizacyjny, który wykonuje się przed konstruktorem:
{ put("firstName", "John"); put("lastName", "Smith"); put("organizations", new HashMap(){{ put("0", new HashMap(){{ put("id", "1234"); }}); put("abc", new HashMap(){{ put("id", "5678"); }}); }
W konsekwencji NIE tworzymy obiektu typu HashMap, tworzymy natomiast obiekt klasy anonimowej która dziedziczy po HashMap z blokiem inicjalizacyjnym.
Dowód:
Helper helper = new Helper(); Map map = helper.doubleCurlyBraces(); System.out.println(map.getClass());
wynik:
class pl.javaleader.Helper$1
Utworzona klasa ma zatem niejawny wskaźnik this do otaczającej klasy zewnętrznej – jakie niesie ten sposób inicjalizacji obiektów konsekwencje? Konsekwencją jest oczywiście wyciek pamięci!
public class Helper { public Map doubleCurlyBraces() { Map source = new HashMap() {{ put("firstName", "John"); put("lastName", "Smith"); put("organizations", new HashMap() {{ put("0", new HashMap() {{ put("id", "1234"); }}); put("abc", new HashMap() {{ put("id", "5678"); }}); }}); }}; return source; } }
public class Start { public static void main(String[] args) throws NoSuchFieldException, IllegalAccessException { Helper helper = new Helper(); Map map = helper.doubleCurlyBraces(); Field field = map.getClass().getDeclaredField("this$0"); field.setAccessible(true); System.out.println(field.get(map).getClass()); Field field2 = map.get("organizations").getClass().getDeclaredField("this$1"); field2.setAccessible(true); System.out.println(field2.get(map.get("organizations")).getClass()); } }
wynik:
class pl.javaleader.Helper class pl.javaleader.Helper$1
Nie stosujmy tego typu inicjalizacji!
Leave a comment