3

비즈니스 의사 결정 응용 프로그램의 경우 지연 초기화로 값 비싼 값을 캐시해야하는 많은 경우가 발생합니다. 그래서 나는 게으른 초기화를 캡슐화하기 위해 generics와 Supplier lambda를 사용했다.게으른 초기화 공급자에서 "this"를 참조 하시겠습니까?

import java.util.function.Supplier; 

public final class LazyProperty<T> { 
    private final Supplier<T> supplier; 
    private volatile T value; 

    private LazyProperty(Supplier<T> supplier) { 
     this.supplier = supplier; 
    } 
    public T get() { 
     if (value == null) { 
      synchronized(this) { 
       if (value == null) { 
        value = supplier.get(); 
       } 
      } 
     } 
     return value; 
    } 

    public static <T> LazyProperty<T> forSupplier(Supplier<T> supplier) { 
     return new LazyProperty<T>(supplier); 
    } 
} 

그러나 나는 그것을 만든 후 개체는 일반적으로 (이 속성을 계산할 수 있기 때문에, 내가 개체가 생성 된 후까지 속성을 초기화 할 수 없습니다 경우이 사용할 수 있도록하고 싶습니다 자체 또는 다른 객체의 컨텍스트가 필요함). 그러나 이는 종종 공급자 기능에서 this에 대한 참조를 필요로합니다. 올바른 보조 장비에 this 기준에 의한 임의의 부분 구조의 문제가 없어야만큼 I가 MyClass합니다 (getExpensiveVal() 방법을 통해) 생성 후 LazyProperty의 get() 함수만을 호출 보장 할 수

public class MyClass { 
    private final LazyProperty<BigDecimal> expensiveVal = 
     LazyProperty.forSupplier(() -> calculateExpensiveVal(this)); 

    public BigDecimal getExpensiveVal() { 
     return expensiveVal.get(); 
    } 
} 

, ? 당신은 어떤 문제가 안하지만 더 명시 적으로처럼 아마 당신의 클래스를 작성합니다 보여 작은 코드를 기반으로

+0

'this'는 람다가 아니라 'MyClass'를 참조하기 때문에 반드시 문제가있는 것은 아닙니다. 문제가 생겼습니까? 아니면 개념적으로 불투명하다고 우려 했습니까? – Makoto

+0

아니요. 문제가 없었습니다. 그렇기 때문에 저는 개념적으로 불건전하다는 것에 대해 걱정했습니다. 나는 초기화 나 건설에서'this'에 대한 참조가 부분적으로 구성된 객체를 노출 할 수 있기 때문에 위험하다는 것을 항상 읽었습니다. 그러나 함수형 프로그래밍에서는 'this'(MyClass를 참조)에 대한 참조를 포함하는 임의의 램다가 생성시 실행되지 않는 한 무해하다는 점을 이해할 수 있습니다. – tmn

+1

예. 그래서 여기에 문제가 보이지 않습니다. 당신은 어디에서든지 생성자에서'expensiveVal' 필드를 사용하지 않기 때문에, 당신이 그 문제에 부딪 힐 것이라고 나는 믿지 않습니다. 'calculateExpensiveVal'가 당신이 원하는 올바른 타입을 사용할 수 있도록해야하기 때문에 타입 안전성에 흥미로운 시간을 가질 것입니다. – Makoto

답변

1

의 구현에 의존 한 문제가됩니다 코드 방법 calculateExpensiveVal.

  1. calculateExpensiveVal 당신이 NullPointerException이 얻을 것이다 MyClass에의 통과 기준에 getExpensiveVal를 호출하는 경우

    .

  2. 경우 calculateExpensiveVal는 다시 포인트 1.

으로 이와 같은 현상이 발생 될 수 있습니다하지만 당신은 보장 경우 calculateExpensiveVal이 중 하나를 수행하지 않는 스레드를 만들고 MyClass에의 참조를 전달 그런 다음 코드는 스레드 안전성 관점에서 올바르게 나타납니다. MyClass에 부분적으로 말을 한 후 때문에 JMM

에서 제공하는 최종 gaurantees의 를 만들어 보지 않을 것이다 비록 당신의 * calculateExpensiveVal 어느 하나 만 getExpensiveVal에 문제가있는가는 모두 그 포인트를 사용할 수있다 NullPointerException가있는 메소드

lazyProperty.get 메서드는 이미 스레드로부터 안전하므로 문제가 될 수 있습니다. 당신은 항상 완전히 때문에 최종 키워드 공급 업체 객체를 구축 볼 수 있기 때문에

(다른 스레드에 '이'참조를 탈출하지 않은 경우에만) 당신은 이미 소요 필드에 휘발성 사용했다 완벽하게 구성된 보보의 보 개체입니다.

+0

물론. 내 초록 예제를 SSCCE와 비슷하게 만들 수는 있지만, 다중 스레드 오용의 가능성과 잠재력 모두에 절대적으로 동의합니다. – tmn

2

:

public class MyClass { 
    private final LazyProperty<BigDecimal> expensiveVal; 

    public MyClass() { 
     this.expensiveVal = LazyProperty.forSupplier(() -> calculateExpensiveVal(MyClass.this)); 
    } 

    public BigDecimal getExpensiveVal() { 
     return expensiveVal.get(); 
    } 
}