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

Your email address will not be published.


*