2017-02-22 1 views
3

스레드 안전 선택 사양 인 지연 초기화를 요구하는 일반 기능을 작성하려고합니다. 값이 최종 값이 아니며 이미 setter를 통해 설정되었을 수 있으므로 표준 패턴을 사용할 수 없습니다.Java 7에서 패턴 생성 또는 생성 하시겠습니까?

final LazyInitializer<List<String>> value = new LazyInitializer<>(ArrayList<String>::new); 

이 스레드 안전, 처리 할 수 ​​

public class LazyInitializer<T> { 

    protected final Supplier<T> initializer; 
    protected AtomicReference<T> value = new AtomicReference<>(); 

    public LazyInitializer(final Supplier<T> initializer) { 
    this.initializer = initializer; 
    } 

    public T get() { 
    T result = this.value.get(); 
    if (result == null) { 
     this.value.compareAndSet(null, this.initializer.get()); 
     result = this.value.get(); 
    } 
    return result; 
    } 

    public void setValue(final T value) { 
    this.value.set(value); 
    } 

} 

당신은 다음과 같이이 클래스를 사용합니다 :

자바 8 년 전 공급 업체와 LazyInitializer 일반적인를 작성하여 것으로 해결 setters는 매우 낮은 오버 헤드를 가지며 특히 : 상용구 코드가 거의 필요하지 않습니다.

그러나 Java 7을 사용하도록 강요 받았으며 Java 7이 Suppliers를 사용할 수 없으므로 추악한 코드를 많이 작성해야하므로 똑같이 우아한 솔루션을 찾을 수없는 것 같습니다. 또한 일반 클래스 값 (예 : ArrayList<String>)을 사용하는 경우 제네릭은 정확한 클래스를 제공하지 않으면 해당 클래스를 인스턴스화 할 수 없습니다.

필자도 추한 코드를 작성하거나 내 능력을 뛰어 넘는 반사 마법을 사용하도록 강요 받았다거나, 누락 된 Java 7에서 우아한 방법으로 LazyInitializer 클래스를 작성하는 방법이 있습니까?

편집 : 다음과 같이 요른 Vernee에서 응답을 사용하여이 나는 자바 7에서 작동하도록 클래스를 수정 :

final LazyInitializer<List<String>> value = new LazyInitializer<>(ArrayList.class); 
: 다음 우아 같이이라고 (다시 한번) 할 수

public class LazyInitializer<T> { 

    protected final Class<?> clazz; 
    protected AtomicReference<T> value = new AtomicReference<>(); 

    public LazyInitializer(final Class<?> clazz) { 
    this.clazz = clazz; 
    } 

    public T get() { 
    T result = this.value.get(); 
    if (result == null) { 
     this.value.compareAndSet(null, constructNew()); 
     result = this.value.get(); 
    } 
    return result; 
    } 

    public void setValue(final T value) { 
    this.value.set(value); 
    } 

    protected T constructNew() { 
    try { 
     return (T) clazz.newInstance(); 
    } catch (InstantiationException | IllegalAccessException ex) { 
     throw new IllegalStateException(ex); 
    } 
    } 
} 

이 클래스는 제공된 클래스가 실제로 일치하는지 (Generics) 때문에 더 이상 확인할 수 없으며 기본 생성자에서만 작동합니다. 그러나 적어도 그것은 나의 경우를 해결합니다.

+0

잘못되었거나 Java8 예제 코드의 첫 번째'this.value.get()'과 if 절을 건너 뛸 수 있습니까? – Alexander

+0

'value == null'의 경우, 값은 초기 값으로 설정되어야합니다. 그래서, 당신은 그것을 건너 뛸 수 없습니다. – TwoThe

+1

@TwoThe 실수로 코드가 공급자 실행을 두 번 이상 트리거 할 수 있습니까? 'compareAndSet'에 대한 매개 변수 평가가 함수 자체에 대한 원자 적 평가가 아니기 때문입니다. – chimmi

답변

1

한 당신이 돌아가 그래서 만약 물론,이 X로 코드의 양을 감소한다는 것입니다 :

public static <T> Supplier<List<T>> newArrayListSupplier() { 
    return new Supplier<List<T>>() { 
    @Override public List<T> get() { return new ArrayList<>(); } 
    }; 
} 

귀하의 최종 코드는된다 , 그것은 X에 의해 코드의 양을 다시 증가시킬 것입니다.functor에 코드를 래핑해야하는 경우 anonymous 클래스가 imho를 수행하는 가장 좋은 방법입니다.

리플렉션을 사용하는 덜 효율적이고 해킹 방법이 있습니다. 그리고 의도 한대로 사용하면 예외를 발생시키지 않습니다.

public static <T> Supplier<T> constructorLookup(Class<?> rawtype) { 
    try { 
     Constructor<?> cons = rawtype.getConstructor(); 

     return new Supplier<T>() { 
      @SuppressWarnings("unchecked") 
      @Override 
      public T get() { 
       try { 
        return (T) cons.newInstance(); 
       } catch (InstantiationException | IllegalAccessException | IllegalArgumentException 
         | InvocationTargetException e) { 
        throw new IllegalStateException(e); 
       } 
      }    
     }; 

    } catch (NoSuchMethodException | SecurityException e) { 
     throw new IllegalArgumentException(e); 
    }  
} 

결과를 :

interface Supplier<T> { 
    T get(); 
} 

이 그럼 당신은 런타임에 검색을하는 팩토리 메소드가 있습니다

동적 생성자 조회를 할 수있는, 당신은 여전히 ​​Supplier 유형을 필요 했어 코드는 다음과 같을 것입니다 :

LazyInitializer<List<String>> value 
    = new LazyInitializer<>(constructorLookup(ArrayList.class)); 

현재이 오류는 defaul t 생성자가되지만 인수를 사용하여 확장 할 수도 있습니다.

+0

Hacky, 그늘진 작품. 나는 그것을 좋아한다. ;) – TwoThe

2

Guava을 사용할 수있는 경우 Supplier 클래스를 포함하는 최신 Java-7 호환 버전을 사용할 수 있습니다.이 버전은 Java 8의 Supplier과 동일한 서명을 갖습니다. 가져 오기 만 다르므로 java.util.function.Supplier 대신 com.google.common.base.Supplier을 입력하십시오. 당신은 구아바를 사용하지 않을 경우

, 당신은 여전히 ​​8의 Supplier 자바와 같은 서명이 자신의 Supplier를 작성할 수

public interface Supplier<T> { 
    T get(); 
} 

를 그런 다음 사용 중 Supplier 다음을 쓸 수 있습니다 :

제네릭이 여러분에게 많은 불편을 겪게되면 실제로 다음을 작성한 다음 마음대로 재사용 할 수 있습니다. , 람다/방법의 심판에 대한 좋은 일들

final LazyInitializer<List<String>> value = new LazyInitializer<>(MyClass.<String>newArrayListSupplier()); 
+0

힌트를 보내 주셔서 감사합니다. 그러나 특히 코드 양을 줄이려 고합니다. JSON-POJO 객체에서 값을 게으른 것으로 설정하는 여러 장소가 있으며 익명 클래스로 모든 작업을 수행하면 현재 가지고있는 코드의 양이 늘어납니다. 그리고 모든 장소는 다른 클래스를 가지고 있기 때문에 List에 대한 솔루션을 사용할 수 없습니다. – TwoThe

+0

글쎄, 새로운 객체를 느리게 생성 할 수있는 다른 방법이 없다는 것에 대해 유감스럽게 생각합니다. 이러한 패턴은 람다를 시뮬레이션하는 데 필요합니다 (결국 귀하의 요청입니다). –