[java] Java에서 Double Brace 초기화 란 무엇입니까?

{{ ... }}Java에서 Double Brace 초기화 구문 ( ) 이란 무엇입니까 ?



답변

이중 괄호 초기화는 지정된 클래스 ( 외부 괄호) 에서 파생 된 익명 클래스를 작성하고 해당 클래스 내에 초기 괄호 ( 내부 괄호)를 제공합니다. 예 :

new ArrayList<Integer>() {{
   add(1);
   add(2);
}};

이 이중 괄호 초기화를 사용하면 익명의 내부 클래스를 만들게됩니다. 생성 된 클래스에는 this주변 외부 클래스에 대한 암시 적 포인터가 있습니다. 일반적으로 문제는 아니지만 직렬화 또는 가비지 수집과 같은 일부 환경에서는 슬픔을 유발할 수 있으므로이를 알고 있어야합니다.


답변

누군가 이중 괄호 초기화를 사용할 때마다 새끼 고양이가 죽습니다.

구문이 다소 독특하지 않고 실제로 관용적이지는 않지만 (물론 맛은 물론 논쟁의 여지가 있습니다), 응용 프로그램에서 두 가지 중요한 문제가 불필요하게 생성 되고 있습니다 .

1. 당신은 너무 많은 익명 클래스를 만들고 있습니다

이중 괄호 초기화를 사용할 때마다 새 클래스가 작성됩니다. 예를 들어이 예 :

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");
        }});
    }});
}};

…이 클래스를 생성합니다 :

Test$1$1$1.class
Test$1$1$2.class
Test$1$1.class
Test$1.class
Test.class

그것은 클래스 로더에 대한 약간의 오버 헤드입니다. 아무것도 없습니다! 물론 한 번 수행하면 초기화 시간이 많이 걸리지 않습니다. 그러나 엔터프라이즈 애플리케이션 전체에서이 2 만 번을 수행한다면 … “구문 설탕”에 대한 모든 힙 메모리?

2. 메모리 누수가 발생할 가능성이 있습니다!

위의 코드를 가져와 메소드에서 해당 맵을 리턴하면 해당 메소드의 호출자가 의심 할 여지없이 가비지 콜렉션 할 수없는 매우 많은 자원을 보유하고있을 수 있습니다. 다음 예제를 고려하십시오.

public class ReallyHeavyObject {

    // Just to illustrate...
    private int[] tonsOfValues;
    private Resource[] tonsOfResources;

    // This method almost does nothing
    public Map quickHarmlessMethod() {
        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;
    }
}

반환 된 Map에의 닫는 인스턴스에 대한 참조가 포함됩니다 ReallyHeavyObject. 다음과 같은 위험을 감수하고 싶지 않을 것입니다.

바로 메모리 누수

http://blog.jooq.org/2014/12/08/dont-be-clever-the-double-curly-braces-anti-pattern/의 이미지

3. Java에 맵 리터럴이 있다고 가정 할 수 있습니다.

실제 질문에 대답하기 위해 사람들은이 구문을 사용하여 Java에 기존 배열 리터럴과 비슷한 맵 리터럴과 같은 것으로 가장했습니다.

String[] array = { "John", "Doe" };
Map map = new HashMap() {{ put("John", "Doe"); }};

어떤 사람들은 이것이 문법적으로 자극적 인 것을 발견 할 수 있습니다.


답변

  • 첫 번째 버팀대는 새로운 Anonymous Inner Class를 만듭니다.
  • 두 번째 괄호 세트는 클래스의 정적 블록과 같은 인스턴스 이니셜 라이저를 작성합니다.

예를 들면 다음과 같습니다.

   public class TestHashMap {
    public static void main(String[] args) {
        HashMap<String,String> map = new HashMap<String,String>(){
        {
            put("1", "ONE");
        }{
            put("2", "TWO");
        }{
            put("3", "THREE");
        }
        };
        Set<String> keySet = map.keySet();
        for (String string : keySet) {
            System.out.println(string+" ->"+map.get(string));
        }
    }

}

작동 원리

첫 번째 버팀대 는 새로운 Anonymous Inner Class를 만듭니다. 이 내부 클래스는 부모 클래스의 동작에 액세스 할 수 있습니다. 따라서 우리의 경우 실제로 HashSet 클래스의 서브 클래스를 작성하므로이 내부 클래스는 put () 메소드를 사용할 수 있습니다.

그리고 두 번째 중괄호 세트는 인스턴스 이니셜 라이저 일뿐입니다. 핵심 Java 개념을 생각 나게하면 구조체와 비슷한 중괄호로 인해 인스턴스 이니셜 라이저 블록을 정적 이니셜 라이저와 쉽게 연결할 수 있습니다. 정적 이니셜 라이저에는 static 키워드가 추가되고 한 번만 실행된다는 점만 다릅니다. 얼마나 많은 객체를 생성하든


답변

이중 괄호 초기화의 재미있는 응용 프로그램은 여기 Dwemthy ‘s Array in Java를 참조하십시오 .

발췌

private static class IndustrialRaverMonkey
  extends Creature.Base {{
    life = 46;
    strength = 35;
    charisma = 91;
    weapon = 2;
  }}

private static class DwarvenAngel
  extends Creature.Base {{
    life = 540;
    strength = 6;
    charisma = 144;
    weapon = 50;
  }}

그리고 지금, BattleOfGrottoOfSausageSmells그리고 … chunky 베이컨을 준비하십시오 !


답변

Java에는 “Double Brace initialize”와 같은 것이 없다는 것을 강조하는 것이 중요하다고 생각합니다 . Oracle 웹 사이트에는이 용어가 없습니다. 이 예제에는 익명 클래스와 이니셜 라이저 블록이라는 두 가지 기능이 함께 사용됩니다. 이전 이니셜 라이저 블록이 개발자들에 의해 잊혀져 서이 주제에서 약간의 혼란을 야기한 것 같습니다. 오라클 문서 에서 인용 :

인스턴스 변수의 이니셜 라이저 블록은 정적 이니셜 라이저 블록과 유사하지만 정적 키워드는 없습니다.

{
    // whatever code is needed for initialization goes here
}


답변

1- 이중 괄호와 같은 것은 없습니다 :
이중 괄호 초기화와 같은 것이 없다는 것을 지적하고 싶습니다. 일반적인 하나의 버팀대 초기화 블록 만 있습니다. 두 번째 괄호 블록은 초기화와 관련이 없습니다. 답은 두 괄호가 무언가를 초기화한다고 말하지만 그렇지 않습니다.

2- 그것은 익명 클래스뿐만 아니라 모든 클래스에 관한 것입니다.
거의 모든 대답은 익명의 내부 클래스를 만들 때 사용되는 것이라고 말합니다. 나는 그 대답을 읽는 사람들이 익명의 내부 클래스를 만들 때만 사용된다는 인상을 얻을 것이라고 생각합니다. 그러나 모든 클래스에서 사용됩니다. 그 답변을 읽는 것은 익명 클래스 전용의 새로운 특수 기능이며 오해의 소지가 있다고 생각합니다.

3- 목적은 단지 새로운 개념이 아니라 서로 괄호를 배치하는 것입니다.
계속해서,이 질문은 두 번째 개방 브래킷이 첫 번째 개방 브래킷 바로 뒤에있는 상황에 대해 이야기합니다. 일반 클래스에서 사용하면 일반적으로 두 개의 중괄호 사이에 코드가 있지만 완전히 동일합니다. 따라서 브래킷을 배치해야합니다. 그래서 우리는 이것이 새로운 흥미로운 것이라고 말해서는 안됩니다. 이것이 우리 모두가 알고있는 것이기 때문에 대괄호 사이에 코드로 작성되었습니다. “이중 괄호 초기화”라는 새로운 개념을 만들면 안됩니다.

4- 중첩 된 익명 클래스를 생성하는 것은 두 개의 중괄호와 아무 관련이 없습니다
. 너무 많은 익명 클래스를 생성한다는 주장에 동의하지 않습니다. 초기화 블록 때문에 생성하는 것이 아니라 생성하기 때문입니다. 두 개의 중괄호 초기화를 사용하지 않아도 초기화되므로 이러한 문제는 초기화 없이도 발생할 수 있습니다. 초기화는 초기화 된 객체를 생성하는 요소가 아닙니다.

또한 우리는 존재하지 않는이 “중괄호 초기화”를 사용하여 생성 된 문제 나 심지어 일반적인 하나의 대괄호 초기화로 인해 발생하는 문제에 대해 이야기해서는 안됩니다. 그러나 모든 대답은 독자들에게 익명 클래스를 만드는 것이 잘못이 아니라 “이중 괄호 초기화”라는 악의적 인 존재라는 인상을줍니다.


답변

이중 괄호 초기화의 모든 부정적인 영향을 피하려면 다음과 같이하십시오.

  1. 깨진 “같음”호환성.
  2. 직접 할당을 사용하는 경우 검사가 수행되지 않습니다.
  3. 가능한 메모리 누수.

다음 작업을 수행하십시오.

  1. 이중 괄호 초기화를 위해 별도의 “빌더”클래스를 만드십시오.
  2. 필드를 기본값으로 선언하십시오.
  3. 해당 클래스에 객체 생성 방법을 넣습니다.

예:

public class MyClass {
    public static class Builder {
        public int    first  = -1        ;
        public double second = Double.NaN;
        public String third  = null      ;

        public MyClass create() {
            return new MyClass(first, second, third);
        }
    }

    protected final int    first ;
    protected final double second;
    protected final String third ;

    protected MyClass(
        int    first ,
        double second,
        String third
    ) {
        this.first = first ;
        this.second= second;
        this.third = third ;
    }

    public int    first () { return first ; }
    public double second() { return second; }
    public String third () { return third ; }
}

용법:

MyClass my = new MyClass.Builder(){{ first = 1; third = "3"; }}.create();

장점 :

  1. 간단하게 사용하십시오.
  2. “같음”호환성을 손상시키지 마십시오.
  3. 작성 방법에서 점검을 수행 할 수 있습니다.
  4. 메모리 누수가 없습니다.

단점 :

  • 없음

결과적으로 가장 간단한 Java 빌더 패턴이 있습니다.

github의 모든 샘플보기 : java-sf-builder-simple-example