2011-02-14 5 views
10

Exception 내에 일반 개체 모음을 저장하려고하는데 제네릭을 알아 내는데 문제가 있습니다. 특히, 나는 Hibernate Validator를 사용하고 있으며 어플리케이션의 다른 레이어에서 처리하기 위해 예외 내에 수집 된 위반 목록을 저장하려고합니다. 여기서 예이다 :예외의 인수에서 제네릭 사용

이클립스
Set<ConstraintViolation<User>> violations = validator.validate(user); 
if (violations.size() > 0) { 
    throw new ValidationException("User details are invalid", violations); 
} 

상기 throws 라인이 정의 생성자를 도시하고, I는 ValidationException(String, Set<ConstraintViolation<User>> 생성자 서명을 변경 제안된다. 여기 ValidationException는 다음과 같습니다

public class ValidationException extends Exception { 
    private Set<ConstraintViolation<?>> violations; 

    public ValidationException() { 
    } 
    public ValidationException(String msg) { 
     super(msg); 
    } 
    public ValidationException(String msg, Throwable cause) { 
     super(msg, cause); 
    } 
    public ValidationException(String msg, Set<ConstraintViolation<?>> violations) { 
     super(msg); 
     this.violations = violations; 
    } 
    public Set<ConstraintViolation<?>> getViolations() { 
     return violations; 
    } 
} 

을하지만, 난 그냥 User 검증 이상 사용할 수 있도록 일반적인 ValidationException을 유지하려는. 나는 Set<ConstraintViolation<? extends Object>>도 시도했지만 동일한 결과를 얻습니다.

내가하려는 일을 수행 할 수있는 방법이 있습니까? @SuppressWarning("unchecked") 절대적으로 법적되도록

public class ValidationException extends Exception { 
    private Set<ConstraintViolation<?>> violations; 

    @SuppressWarning("unchecked") 
    public <T> ValidationException(String msg, Set<ConstraintViolation<T>> violations) { 
     super(msg); 
     this.violations = (Set<ConstraintViolation<?>>)(Set<?>) violations; 
    } 
} 

는 지금까지 내가 이해, 선택하지 않은 캐스팅,이 경우 완전히 안전 :

+0

당신이 처음에 일반 될하기 위해 ConstrainsViolation 필요한 이유는 무엇입니까? 가능한 각 위반에 대해 AbstractBase를 상속하는 별도의 클래스를 생성 할 수 없습니까? – Falcon

+1

제네릭 예외 형식을 사용하는 것이 좋은 생각이지만, @axtavt는 언급 한 바와 같이 예외는 일반적 일 수 없다고 생각했습니다. 이 FAQ를 참조하십시오 : http://www.angelikalanger.com/GenericsFAQ/FAQSections/TechnicalDetails.html#FAQ301 – Tauren

+0

@Falcon :'ConstraintsViolation'은 Hibernate Validation 패키지의 일부이며 generics를 사용합니다. 그것은 내 자신의 수업이 아닙니다. – Tauren

답변

12

: 예상대로

public ValidationException(String msg, 
          Set<? extends ConstraintViolation<?>> violations) { 
    super(msg); 
    this.violations = Collections.unmodifiableSet(
     new HashSet<ConstraintViolation<?>>(violations)); 
} 

그런 다음 모든 작동합니다.

이렇게하면 Set을 방어 적으로 복사하여 예외 Set 내부를 변경할 수 있습니다.

+0

감사합니다.이 모양이 좋고 깨끗합니다. – Tauren

1

하나의 추악한 방법은 선택하지 않은 캐스트를 사용하는 것입니다.

반면에이 생성자는 Set<ConstraintViolation<?>>을 매개 변수로 호출 할 수 없습니다.

+0

미운 것이 맞습니다! 그러나 그것이 효과가있는 것처럼 보입니다. 더 이상 컴파일 오류가 발생하지 않습니다. 감사! – Tauren

+0

캐스트가 안전한지 여부는 예외 객체 외부에서 Set이 사용되는 위치와 방법에 따라 다릅니다. 'Set >은'Set >의 상위 유형이 아니며, 후자는'ConstraintViolation '유형의 요소 만 허용하며 (사용자는이 경우에 의존 할 수 있음) 'ConstrainViolation'은 어떤 타입이든 (심지어 혼합 된 것들도) (이것은 당신이이 세트에'ConstraintViolation '을 넣을 수 있음을 의미합니다). 예외에 대한이 사용에서는 예외를 만든 크리에이터가 생성하고 포수는 반복 만하므로 중요한 것은 아닙니다. –

0

Set<ConstraintViolation<User>>에는 ConstraintViolation<User>의 개체 만 포함될 수 있으며 인수 형식 Set<ConstraintViolation<?>>에는 모든 유형의 위반 사항이 포함될 수 있습니다 (따라서 혼합 된 컬렉션 임). 따라서 이것은 하위 유형이 아닙니다. 난 당신이

public <T> ValidationException(String msg, Set<ConstraintViolation<T>> violations); 

로 생성자를 선언 할 수 있지만, 당신은 여전히 ​​예외 변수가 같은 유형이 할 수없는 문제가 (시도하지 않았다) 생각합니다. 새 Set에서 매개 변수의 내용을 복사하고 Collections.unmodifiedSet()을 사용하여 포장하거나 axtavt에서 언급 한 추악한 캐스트를 사용하여이 문제를 해결할 수 있습니다. 여기 내 선호하는 방법 : 당신은 Set<? extends ConstraintViolation<?>>로 매개 변수를 설정 위반을 선언 할 필요가

public <T> ValidationException(String msg, Set<ConstraintViolation<T>> violations) { 
    this.violations = Collections.unmodifiableSet(violations); 
} 
1

집합이 동질이어야한다는 요구가 있다고 가정하면 - violationsX에 대해 Set<ConstraintViolation<X>> 유형이어야합니다. 타입 시스템을 넘어 이유로,

물론
public class ValidationException<T> extends Exception 
    Set<ConstraintViolation<T>> violations; 
    public ValidationException(String msg, Set<ConstraintViolation<T>> violations) 

, 자바의 Throwable의 서브 타입에 대한 것을 허용하지 않습니다

그 작업을 수행하는 가장 자연스러운 방법은 ValidationException 제네릭 확인하는 것입니다.이것은 우리의 잘못이 아니에요, 그래서 우리는 몇 가지 해결 방법을 발명의 무죄입니다

public class ValidationException extends Exception 
{ 
    static class SetConstraintViolation<T> extends HashSet<ConstraintViolation<T>> 
    { 
     SetConstraintViolation(Set<ConstraintViolation<T>> violations) 
     { 
      super(violations); 
     } 
    } 

    // this is homogeneous, though X is unknown 
    private SetConstraintViolation<?> violations; 

    public <T> ValidationException(String msg, Set<ConstraintViolation<T>> violations) 
    { 
     super(msg); 
     this.violations = new SetConstraintViolation<T>(violations); 
    } 

    public <T> Set<ConstraintViolation<T>> getViolations() 
    { 
     return (Set<ConstraintViolation<T>>)violations; 
    } 
} 

void test() 
{ 
    Set<ConstraintViolation<User>> v = ...; 
    ValidationException e = new <User>ValidationException("", v); 
    Set<ConstraintViolation<User>> v2 = e.getViolations(); 
    Set<ConstraintViolation<Pswd>> v3 = e.getViolations(); 
    Set<? extends ConstraintViolation<?>> v4 = e.getViolations(); 
} 

참고 : 전화 사이트 v2의 경우처럼, T 올바른을 제공하는 경우 getViolations()의 캐스팅 만 안전합니다. v3의 경우 캐스트가 잘못되었습니다. 컴파일러는 우리에게 헛된 경고를하지 않았습니다.

v4의 경우와 마찬가지로 콜 사이트는 아마도 T을 정확히 모르고 걱정하지 않습니다. 호출 사이트는 특정 알 수없는 유형의 동종 모음 인 위반을 와일드 카드를 사용하여 좀 더 일반적인 읽기 전용 유형으로 캐스팅 할 수 있습니다. 그것은 꽤 어색합니다. 케이스 v4이 가장 빈번한 유스 케이스 인 경우 Set<ConstraintViolation<?>>을 반환하는 메소드를 제공해야합니다. 직접적으로 violations을 반환 할 수는 없습니다. 안전하지 않습니다. 사본이 필요합니다. v4이 유일한 사용 사례 인 경우이 솔루션은 이전 응답자가 제안한 것과 동일한 솔루션이됩니다.

+0

매우 철저한 답변에 감사드립니다! 귀하의 솔루션은 과도한 복잡성을 추가하는 것으로 보입니다. @ColinD에서 제안한 것과 같은 더 간단한 접근 방식보다 나은 이유가 있습니까? – Tauren

+0

아무 것도 아닌 경우 사고 실험을 위해 균질 한 요구 사항을 부과했습니다. 그것은 당신이 관심을 갖는 것이 아닐 것입니다. – irreputable

0

유형 소거로 인해 catch 블록이 캐치하는 예외의 일반 매개 변수를 테스트 할 수 없으므로 Java throwable을 일반화 할 수 없다고 생각합니다. 캐스트와 기타 등으로 구식 방식으로해야 할 것입니다.

 
class ConstraintViolation extends SomeException { 
  Constraint c; 
    «T extends Constraint» T getConstraint() { return (T) c}; 
} 

… 
UserConstraint uc = theViolation.getConstraint(); 

컴파일러는 실제로 당신이 그것을 요구하고있다 캐스트를 수행 할 수 있음을 경고합니다,하지만 괜찮아 :

당신은 자동적으로 캐스팅 할 수있는 방법을 추가 할 수 있습니다.

0

당신은 할 수 :

Set<ConstraintViolation<User>> violations = validator.validate(user); 
if (violations.size() > 0) { 
    throw new ValidationException("User details are invalid", (Set<ConstraintViolation<?>>) (Set<? extends ConstraintViolation<?>>) violations); 
}