레이블이 lambda인 게시물을 표시합니다. 모든 게시물 표시
레이블이 lambda인 게시물을 표시합니다. 모든 게시물 표시

2017-02-24

enum 네 이놈! - 제네릭과 람다와 enum과 헬게이트

조금 전에 enum들에 널려있는 중복 코드들을 제거하는 와중에 일어난 이야기.

원래대로라면 enum에는 코드값만 넣고 텍스트는 따로 properties에 빼놓는데, 이번 고객은 아아주 특이하게도 그렇게 하면 유지보수하기 귀찮다고 소스에 다 때려박아달랜다.
그래서 다 때려박으면서 예제삼아 코드값 <-> 텍스트 상호전환 코드까지 같이 만들었는데…

누가 (울 회사 직원이라고 말 안할거임) 그걸 그대로 복붙해서 enum 수십 개를 만들었고, CPD는 복붙 고마해라 라고 지랄대고…
해서 코드값 <-> 텍스트 상호전환 하는 부분을 별도의 유틸리티 클래스로 분리했다.

문제는 여기서 발생하는데…

먼저 WithCodeLabel이라는 인터페이스를 만들고 메소드를 정의한 다음, 해당 enum들에 implements를 걸었다.
그리고 맨 처음 유틸리티 클래스를 람다식을 활용하여 만들었다.

public class EnumCodeLabelCache<E extends Enum<E> & WithCodeLabel> {

    private final Map<String, E> codeToLabelMap;
    private final Map<String, E> labelToCodeMap;

    private EnumCodeLabelCache(Class<E> cls) {
        final EnumSet<E> set = EnumSet.allOf(cls);
        codeToLabelMap = set.stream().collect(Collectors.toMap(WithCodeLabel::getCode, Function.identity());
        labelToCodeMap = set.stream().collect(Collectors.toMap(WithCodeLabel::getLabel, Function.identity());
    }
    ...
}

그리고 테스트를 돌렸을 때, 처음 보는 당황스러운 에러가…
java.lang.BootstrapMethodError: call site initialization exception
 at java.lang.invoke.AbstractValidatingLambdaMetafactory.validateMetafactoryArgs(AbstractValidatingLambdaMetafactory.java:233)
 at java.lang.invoke.LambdaMetafactory.metafactory(LambdaMetafactory.java:303)
 at java.lang.invoke.CallSite.makeSite(CallSite.java:302)
 at java.lang.invoke.MethodHandleNatives.linkCallSiteImpl(MethodHandleNatives.java:307)
 at java.lang.invoke.MethodHandleNatives.linkCallSite(MethodHandleNatives.java:297)

…모라고요?

곰곰히 머리를 싸매고 생각하다가 분석을 해보니
enum의 각 값의 RTTI에 WithCodeLabel 인터페이스에 대한 정보가 누락되어 있다는 결과밖에 나오지를 않는다.

그래서 람다 말고 메소드 직접 호출로 변경.

public class EnumCodeLabelCache<E extends Enum<E> & WithCodeLabel> {

    private final Map<String, E> codeToLabelMap = new HashMap<>();
    private final Map<String, E> labelToCodeMap = new HashMap<>();

    private EnumCodeLabelCache(Class<E> cls) {
        final EnumSet<E> set = EnumSet.allOf(cls);
        set.forEach(e -> {
            codeToLabelMap.putIfAbsent(e.getCode(), e);
            labelToCodeMap.putIfAbsent(e.getLabel(), e);
        });
    }
    ...
}

이러니까 잘 된다.

이게 enum만 특히 이런건지, 제네릭 특징인지는 더 살펴봐야 알겠지만, 내가 알 게 뭐야.
Share:

2014-10-06

JPA Entity와 Java 8

아직 하이버네이트에 대해서는 테스트해보지 않았지만, 이클립스링크에서 문제가 발생하므로,  비슷한 처리를 하는 하이버네이트에서도 똑같은 문제가 있을 것이다.

엔티티에 @OneToMany@ManyToMany로 컬렉션(List, Set, Map) 필드가 있을 때, 이 필드가 wrapper로 세팅되어있을 경우 (이클립스링크의 경우 IndirectList, IndirectSet, IndirectMap) 일부 Java 8 기능이 먹지를 않는다.

특히 가장 중요한 stream이 동작하지 않는다.
Aㅏ... 망했어요.
이보시오, 이보시오! JPA양반! 그게 무슨 소리요! Stream이 고자라니!

뭐 별 거 있나, wrap 된거를 꺼내오거나 그냥 새로운 컬렉션을 만들어서 값을 복사해서 쓰면 된다.
참 쉽죠? 너무 쉬워서 예제 따위는 없어요.



뱀발:
엔티티 내부에 Java 8 기능을 우겨넣으면 씐나게 로딩하다가 에러가 날 것이다.
아쉽게도 JPA는 Java 8 기능이 들어간 엔티티는 엔티티로 취급하지 않는다 (...)

물론 그런건 별도의 클래스로 분리해 놓고 써먹으면 그만이다.
Share: