2009-02-05 2 views
3

지도에서 키와 값 유형을 매핑하기 위해 더 복잡한 문제가 있습니다 ('키의 유형 매개 변수로 제한된 값을 가진 Java지도'질문보다). 여기있다 :한계지도 키 및 값 유형 - 좀 더 복잡함

interface AnnotatedFieldValidator<A extends Annotation> { 
    void validate(Field f, A annotation, Object target); 
    Class<A> getSupportedAnnotationClass(); 
} 

지금, 나는 다음과 같은 방법을 쓸 수 있도록,지도에서 유효성 검사기를 저장할 :

validate(Object o) { 
    Field[] fields = getAllFields(o.getClass()); 
    for (Field field: fields) { 
    for (Annotation a: field.getAnnotations()) { 
     AnnotatedFieldValidator validator = validators.get(a); 
     if (validator != null) { 
     validator.validate(field, a, target); 
     } 
    } 
    } 
} 

(나는 가지고 있지 않기 때문에 유형 매개 변수가 생략된다 해결책). 나는 또한 내 유효성 검사기를 등록 할 수 있도록하려면 :

이 (만) 공공 수정 방법으로
public void addValidator(AnnotatedFieldValidator<? extends Annotation> v) { 
    validators.put(v.getSupportedAnnotatedClass(), v); 
} 

, 나는지도가 키 (주석 클래스) 발리의 지원 주석 클래스와 일치하는 항목이 포함 보장 할 수 있습니다.

private Map<Class<? extends Annotation>, AnnotatedFieldValidator<? extends Annotation>> validators; 

내가 제대로 키와 값 (링크 확인 가정을 링크 할 수 없습니다 알고 있어요 인해를 통해서만 액세스 : 나는 유효성 검사기는 다음과 같이지도 선언

: 여기

는 시도이다 addValidator())는, 그래서 캐스트 시도 :

for (Annotation a: field.getAnnotations()) { 
    AnnotatedFieldValidator<? extends Annotation> validator = validators.get(a); 
    if (validator != null) { 
    validator.validate(field, validator.getSupportedAnnotationClass().cast(a), target); 
    } 
} 

을하지만이 작동하지 않습니다 The method validate(Field, capture#8-of ?, Object) in the type AnnotatedFieldValidator<capture#8-of ?> is not applicable for the arguments (Field, capture#9-of ?, Object).

이것이 작동하지 않는 이유를 알 수 없습니다. AnnotatedFieldValidator에는 getSupportedAnnotationClass()의 반환 유형과 validate()의 매개 변수로 사용되는 단일 유형 매개 변수 (A)가 있습니다. 따라서 주석을 supportedAnnotationClass로 캐스팅 할 때 매개 변수로 validate()에 전달할 수 있어야합니다. getSupportedAnnotationClass()의 결과가 validate()의 매개 변수와 다른 유형으로 간주되는 이유는 무엇입니까?

유효성 검사기 선언 및 validate() 메서드에서 와일드 카드를 제거하여 validate() 메서드를 해결할 수 있지만 물론 addValidator()은 컴파일되지 않습니다.

답변

0

답장을 보내 주셔서 감사합니다. 다음 해결책을 찾는데 도움이되었습니다.

flicken의 답은 나에게 다음과 같은 방법을 보여주었습니다. 코드를 매개 변수화 된 방법으로 추출해야했습니다. 메서드에서 validators.get()을 추출하는 대신 전체 유효성 검사 프로세스를 추출 할 수 있습니다. (I 값 맵에 키의 일관성을 제어하기 때문에 내가 OK 가정) 이렇게, 내가 프로그래밍 캐스트를 사용할 수 있습니다

public void validate(Object o) { 
    Field[] fields = getFields(o.getClass()); 
    for (Field field : fields) { 
    Annotation[] annotations = field.getAnnotations(); 
    for (Annotation annotation : annotations) { 
     AnnotatedFieldValidator<? extends Annotation> validator = 
      validators.get(annotation.annotationType()); 
     if (validator != null) { 
     doValidate(field, validator, annotation, o); 
     } 
    } 
    } 
} 

그리고는 doValidate을() 메소드는 다음과 같다 :

private <A extends Annotation> void doValidate(Field field, 
     AnnotatedFieldValidator<A> validator, Annotation a, Object o) { 
    // I assume this is correct following only access to validators Map 
    // through addValidator() 
    A annotation = validator.getSupportedAnnotationClass().cast(a); 
    try { 
     validator.validate(field, annotation, bean, beanName); 
    } catch (IllegalAccessException e) { 
    } 
} 

캐스트가 없습니다 (OK, Class.cast() ... 제외), 체크되지 않은 경고가없고 원시 형식이 아닙니다. 행복합니다.

+0

암시 적 캐스트 (원시 유형을 사용하는 경우)를 명시 적으로 변환했습니다. 그러나 나는이 해결책을 받아 들일 만하다. 나는 타이핑 마술을하기 위해 내 소매에서 또 다른 일반적인 방법을 꺼내는 반사 신경을 가지고 있지 않습니다. – eljenso

+0

당신이 받아 들일만한 해결책을 찾았다면 다행입니다. 나는 그것이 validator Map에 대한 접근을 캡슐화하기 때문에 getValidator 메소드를 추출하는 것을 선호한다. 그러나 최상위 유효성 검사 메소드에서는 좀 더 우아 해 보입니다. – flicken

1

유효성 검사기를 가져 오는 방법을 추출 할 수 있습니다. validators Map에 대한 모든 액세스는 유형 검사 메소드를 통해 이루어 지므로 유형 안전입니다.

protected <A extends Annotation> AnnotatedFieldValidator<A> getValidator(A a) { 
     // unchecked cast, but isolated in method 
     return (AnnotatedFieldValidator<A>) validators.get(a); 
    } 

    public void validate(Object o) { 
     Object target = null; 
     Field[] fields = getAllFields(o.getClass()); 
     for (Field field : fields) { 
      for (Annotation a : field.getAnnotations()) { 
       AnnotatedFieldValidator<Annotation> validator = getValidator(a); 
       if (validator != null) { 
        validator.validate(field, a, target); 
       } 
      } 
     } 
    } 

    // Generic map 
    private Map<Class<? extends Annotation>, AnnotatedFieldValidator<? extends Annotation>> validators; 

(중복하여 삭제 제 제안.)

0

제네릭에서만 컴파일 시간에 존재하는 정보를 제공한다; 런타임에는 모든 정보가 손실됩니다. 제네릭으로 코드를 컴파일 할 때 컴파일러는 모든 제네릭 형식 정보를 제거하고 필요에 따라 캐스트를 삽입합니다. 예를 들어,

List<String> list = new ArrayList<String>(); 
list.add("test"); 
String s = list.get(0); 

컴파일러는 자동적으로 세 번째 줄에 추가로 캐스팅

List list = new ArrayList(); 
list.add("test"); 
String s = (String) list.get(0); 

컴파일 끝나게된다.

런타임 유형 안전을 위해 제네릭을 사용하려고하는 것처럼 보입니다. 이건 불가능 해.라인

validator.validate(field, a, target); 

에서 컴파일러는 검증이 기대 Annotation 어떤 하위 알 수있는 방법이 없습니다.

interface AnnotatedFieldValidator { 
    void validate(Field f, Annotation annotation, Object target); 
    Class<? extends Annotation> getSupportedAnnotationClass(); 
} 

addValidator 다음 또한, 즉 그것의 매개 변수화 유형을 잃게

public void addValidator(AnnotatedFieldValidator v) { 
    validators.put(v.getSupportedAnnotationClass(), v); 
} 

을 :

내가 할 수있는 가장 좋은 변수 A을 유형을 드롭하고 다음과 같이 인터페이스를 선언하는 것입니다 생각 단점은 유효성 검사기가 검증 할 수있는 클래스의 주석을 전달하는지 확인해야한다는 것입니다. 이는 유효성 검사기를 호출하는 코드에서 가장 쉽게 수행 할 수 있습니다 (예 :

if (validator.getSupportedAnnotationClass().isInstance(a)) { 
    validator.validate(field, a, target); 
} 
else { 
    // wrong type of annotation, throw some exception. 
} 
+0

예, 컴파일러가 알 수있는 방법이 없습니다. 이것이 내가 일관성 (addValidator())을 적용하는 방법을 가지고있는 이유입니다. 이제는 일관성이 있다고 가정 했으므로 Class.cast (Object)를 호출 할 수 있어야합니다. 당신의 솔루션은 여전히 ​​객체를 캐스팅해야하는데, 이는 피하고 싶습니다. – Gaetan

0

랍스터 .. 나는 :) 그것을 알아낼 생각

for (Annotation a: field.getAnnotations()) { 
    AnnotatedFieldValidator<? extends Annotation> validator = validators.get(a); 
    if (validator != null) { 
    validator.validate(field, validator.getSupportedAnnotationClass().cast(a), target); 
    } 
} 

편집 : (변경 내 관점) 당신은 런타임에서 볼 때

이 의미가 정확 관점은 아니지만 COMPILE TIME에 없습니다.

여기서 컴파일러validator.getSupportedAnnotationClass().cast()Class<? extends Annotation>이라고 가정합니다.

당신이 전화 할 때 :

validator.validate(field, validator.getSupportedAnnotationClass().cast(a), target); 

컴파일러이 인수로 Class<? extends Annotation> 기대하고있다.

여기에 문제가 있습니다. 컴파일러 관점에서 볼 때,이 유형은 런타임에서 2 가지 유형이 될 수 있지만,이 경우에는 의미론이이를 허용하지 않습니다.

+0

나는 당신과 의견이 다릅니다 : AnnotatedFieldValidator .getSupportedAnnotationClass()를 호출하면 Class 이 나옵니다. 따라서이 클래스의 cast()는 Deprecated의 인스턴스를 반환해야합니다. – Gaetan

+0

업데이트 된 응답을 확인하십시오. –

0

제네릭이 까다로운 자료이므로 반응을 통해 뭔가를 배울 수 있기 때문에이 내용을 알려 드리겠습니다. 그래서, 내가 틀렸다면 나를 바로 잡으십시오..

먼저 소스 코드를 하나의 블록에 넣으십시오. (편의상 AFVAnnotatedFieldValidator 개명)

interface AFV<A extends Annotation> 
{ 
    void validate(Field f, A annotation, Object target); 
    Class<A> getSupportedAnnotationClass(); 
} 

private Map<Class<? extends Annotation>, AFV<? extends Annotation>> validators; 

public void validate(Object o) { 
    Field[] fields = o.getClass().getDeclaredFields(); 
    for (Field field: fields) { 
    for (Annotation a: field.getAnnotations()) { 
     AFV<? extends Annotation> validator = validators.get(a.getClass()); 
     if (validator != null) { 
     validator.validate(field, a, o); 
     } 
    } 
    } 
} 

public void addValidator(AFV<? extends Annotation> v) { 
    validators.put(v.getSupportedAnnotationClass(), v); 
} 

문제는 당신이 필드의 주석을 반복 할 때, 컴파일러가 추론 할 수있는 유일한 유형 Annotation, 그것의하지 전문화 것입니다. 이제 Map에서 올바른 AFV을 가져 와서 validate을 호출하면 두 번째 매개 변수에 유형 충돌이 발생합니다. AFVAnnotation의 특정 하위 클래스를보고 싶어하지만이 매개 변수는 매개 변수화되었지만 Annotation, 너무 약하다.다음과 같이

당신은 이미 유효성 검사기를 추가 할 수있는 유일한 방법은 addValidator 방법으로 경우에, 당신은 내부적를 통해 유형의 안전을 확인하고 다시 작성할 수 있습니다, 자신의 말처럼

interface AFV<A extends Annotation> 
{ 
    void validate(Field f, A annotation, Object target); 
    Class<A> getSupportedAnnotationClass(); 
} 

private Map<Class<?>, AFV<?>> validators; 

public void validate(Object o) 
{ 
    Field[] fields = o.getClass().getDeclaredFields(); 
    for (Field field : fields) 
    { 
     for (Annotation a : field.getAnnotations()) 
     { 
      // raw type to keep compiler happy 
      AFV validator = validators.get(a.getClass()); 
      if (validator != null) 
      { 
       validator.validate(field, a, o); // statically unsafe 
      } 
     } 
    } 
} 

public void addValidator(AFV<?> v) 
{ 
    validators.put(v.getSupportedAnnotationClass(), v); 
} 

참고가 호출 validator.validate 이제 정적으로 안전하지 않으며 컴파일러에서 경고를 보냅니다. 그러나 당신이 그걸로 살 수 있다면, 이것은 할 수 있습니다.

+0

나는 '극단 주의자'라고 생각하지만 원시 타입이없는 해결책을 선호한다. – Gaetan

+0

나는 너를 확신 할 수 없다. 사용할 수있는 유형 정보가 부족하기 때문에 매개 변수화 된 AFV가있는 경우 캐스팅이 필요합니다. 여기에있는 제네릭은 단순히 장면 뒤에서 "자동 캐스팅"하는 데 사용됩니다. 제네릭을 버리면 Class.isInstance를 사용하여 유형 검사를 수행해야합니다. – eljenso

관련 문제