반응형

아래와 같은 초기화 코드를 보게 되었습니다.

Map<Integer, Integer> map1 = new HashMap<>() {{
       put(1, 1);
       put(2, 2);
}};

인스턴스 생성과 함께 초기화가 가능한 것처럼 보여서 코드도 쉽고 간단해보이지만,

지양해야 할 안티 패턴 중 하나였습니다.

일반적으로 컬렉션을 초기화할 때 사용하는, 아래와 같은 코드를 컴파일 해보겠습니다.

public class DoubleBraceTest {

    public static void main(String[] args) {
        Map<Integer, Integer> map1 = new HashMap<>();
        map1.put(1,1);
        map1.put(2,2);

        Map<Integer, Integer> map2 = new HashMap<>();
        map2.put(1,1);
        map2.put(2,2);
    }
}

결과로 나온 클래스 파일은 아래와 같습니다.

$ ls
DoubleBraceTest.class

1개의 클래스 파일이 만들어졌습니다.

이번에는 double brace를 사용하여 초기화하는 코드를 컴파일해보겠습니다.

public class DoubleBraceTest {

    public static void main(String[] args) {
        Map<Integer, Integer> map1 = new HashMap<>() {{
           put(1, 1);
           put(2, 2);
        }};

        Map<Integer, Integer> map2 = new HashMap<>() {{
            put(1, 1);
            put(2, 2);
        }};
    }
}

결과는 아래와 같습니다.

$ ls
'DoubleBraceTest$1.class'  'DoubleBraceTest$2.class'   DoubleBraceTest.class

익명 내부 클래스를 이용한 적도 없는데, 2개의 익명 내부 클래스 파일이 생성되어있습니다.

초기화를 위해 중괄호 블록을 넣는 코드마다, 컴파일 타임에 하나의 새로운 익명 내부 클래스 파일이 생성되고 있는 것입니다.

이러한 까닭에 double brace initialization을 하면,

JVM이 새로운 클래스 파일을 읽어야하는 부담도 생기고, 클래스 파일이 늘어남에 따라 저장 공간에 부담도 생기게 됩니다. 또, 바깥 인스턴스에 대한 숨겨진 참조를 가지기 때문에 메모리 누수를 일으킬 수도 있습니다.

이에 대한 대안으로 다음과 같이 별도로 초기화하거나, 자바 9의 컬렉션 팩토리 메소드를 이용할 수 있겠습니다.

Map<Integer, Integer> map = new HashMap<>();
map.put(1, 1);
map.put(2, 2);

Map<Integer, Integer> map = Map.of(1, 1, 2, 2);

Set<Integer> set = Set.of(1, 2);

List<Integer> list = List.of(1, 2);
반응형

+ Recent posts