<aside> 💡 필요할 때까지 지연 초기화를 하지 말것.

</aside>

지연초기화

지연 초기화 (lazy initialization)은 필드의 초기화 시점을 그 값이 처음 필요할 때까지 늦추는 기법이다.

대부분의 상황에서는 일반적인 초기화가 지연 초기화보다 낫다.

//일반적인 초기화
private final FieldType field = computeFieldValue();

//인스턴스 필드의 지연 초기화 - synchronized 접근자 방식
//지연 초기화가 초기화 순환성을 깨뜨릴 것 같다면 synchronized를 단 접근자 사용
// 이 방법이 가장 간단, 명확한 대안이다.
private FieldType field2;
private synchronized FieldType getField2() {
    if (field2 == null)
        field2 = computeFieldValue();
    return field2;
}

//정적 필드용 지연 초기화 홀더 클래스 관용구
//성능 때문에 정적 필드를 지연 초기화해야 한다면 지연 초기화 홀더 클래스 관용구 사용
//클래스는 클래스가 처음 쓰일 때 비로소 초기화된다는 특성 이용한 관용구.
private static class FieldHolder {
    static final FieldType field = computeFieldValue();
}

//getField가 처음 호출되는 순간 FieldHolder.field가 처음 읽히면서
//FieldHolder 클래스 초기화를 촉발함
//getField 메서드가 필드에 접근하면서 동기화를 전혀 하지 않아 성능이 느려질 거리가 없다.
private static FieldType getField() { return FieldHolder.field; }

// 인스턴스 필드 지연 초기화용 이중검사 관용구
// 성능 때문에 인스턴스 필드를 초기화해야 한다면 사용
// 초기화된 필드에 접근할 때의 동기화 비용을 없애준다.
//필드가 초기화된 후로는 동기화하지 않아서 해당 필드는 반드시 volatile로 선언해야함
private volatile FieldType field4;

private FieldType getField4() {
		//필드가 이미 초기화된 상황에서 필드를 딱 한 번만 읽도록 보장함 
    FieldType result = field4;
    if (result != null)    // 첫 번째 검사 (락 사용 X)
        return result;

    synchronized(this) {
        if (field4 == null) // 두 번째 검사 (락 사용)
            field4 = computeFieldValue();
        return field4;
    }
}

// 단일검사 관용구 - 초기화가 중복해서 일어날 수 있다.
// 반복해서 초기화해도 상관없는 인스턴스 필드를 지연 초기화해야 할 때 사용
// 이중검사 관용구에서 두 번째 검사 생략
private volatile FieldType field5;

private FieldType getField5() {
    FieldType result = field5;
    if (result == null)
        field5 = result = computeFieldValue();
    return result;
}

private static FieldType computeFieldValue() {
    return new FieldType();
}