2013-03-12 7 views
10

Ich habe folgenden BeanValidation Code, der in Ordnung, und erlaubt arbeitet zu validieren, dass eine Bohne kommentierte mit:Generika und Klasse <? Enum erstreckt <?>>, EnumSet.allOf (Klasse) vs class.getEnumConstants()

@EnumValue(enumClass = MyTestEnum.class) 
    private String field; 

    public enum MyTestEnum { 
    VAL1, VAL2; 
    } 

nur validiert werden Will wenn der Feldwert "VAL1" oder "VAL2" ist.

public class EnumNameValidator implements ConstraintValidator<EnumValue, String> { 

    private Set<String> AVAILABLE_ENUM_NAMES; 

    @Override 
    public void initialize(EnumValue enumValue) { 
    Class<? extends Enum<?>> enumSelected = enumValue.enumClass(); 
    Set<? extends Enum<?>> enumInstances = Sets.newHashSet(enumSelected.getEnumConstants()); 
    AVAILABLE_ENUM_NAMES = FluentIterable 
      .from(enumInstances) 
      .transform(PrimitiveGuavaFunctions.ENUM_TO_NAME) 
      .toImmutableSet(); 
    } 

    @Override 
    public boolean isValid(String value, ConstraintValidatorContext context) { 
    if (value == null) { 
     return true; 
    } else { 
     return AVAILABLE_ENUM_NAMES.contains(value); 
    } 
    } 

} 

Was ich nicht verstehe ist, warum mein erster Versuch fehlgeschlagen ist. Unter Verwendung anstelle des enumSelected.getEnumConstants() über den folgenden Code:

Set<? extends Enum<?>> enumInstances = EnumSet.allOf(enumSelected); 

Intellij 12 keine Fehler nicht markieren, aber der Compiler sagt:

java: method allOf in class java.util.EnumSet<E> cannot be applied to given types; 
    required: java.lang.Class<E> 
    found: java.lang.Class<capture#1 of ? extends java.lang.Enum<?>> 
    reason: inferred type does not conform to declared bound(s) 
    inferred: capture#1 of ? extends java.lang.Enum<?> 
    bound(s): java.lang.Enum<capture#1 of ? extends java.lang.Enum<?>> 

Ich verstehe das Problem nicht, und ich habe auch, dass Code, der gut arbeitet:

private static <T extends Enum<T> & EnumAlternativeName> T safeGetByAlternativeName(Class<T> enumClass, String alternativeName) { 
    for (T t : EnumSet.allOf(enumClass)) { 
     if (t.getAlternativeName().equals(alternativeName)) { 
     return t; 
     } 
    } 
    return null; 
    } 
+0

Zugehöriges Ticket: http://stackoverflow.com/questions/5548091/problem-when-trying-to-use-generics –

+0

Auch dieses: http://stackoverflow.com/questions/3546745/multiple-wildcards-on- a-generisch-m ethods-macht-java-compiler-und-ich-sehr-confu – assylias

+1

@ChristopheRoussy: Ticket? Ist StackOverflow jetzt ein Support System :) – mellamokb

Antwort

8

Meine Vermutung ist, dass in ? extends Enum<?> der zwei ? könnte anders sein, während allOf eine T extends Enum<T> erwartet, wo beide T die gleichen sind.

Betrachten wir zum Beispiel den folgenden Code:

static enum MyEnum {} 
static class EnumValue<T extends Enum<T>> { 
    Class<T> enumClass; 
    EnumValue(Class<T> enumClass) { 
     this.enumClass = enumClass; 
    } 
    Class<T> enumClass() { return enumClass; } 
} 

Diese Zeilen kompiliert:

EnumValue<?> enumValue = new EnumValue(MyEnum.class); // raw constructor 
Set<? extends Enum<?>> enumInstances = EnumSet.allOf(enumValue.enumClass()); 

, weil wir wissen, dass die beiden T in enumValue.enumClass() gleich sind, aber diese werden nicht:

EnumValue enumValue = new EnumValue(MyEnum.class); 
Class<? extends Enum<?>> enumSelected = enumValue.enumClass(); 
Set<? extends Enum<?>> enumInstances = EnumSet.allOf(enumSelected); 

weil Sie Informationen verloren haben Verwenden eines Class<? extends Enum<?>> als Zwischenschritt.

+0

danke, ich denke, ich habe keine Wahl, denn ich kann nicht machen, dass Enum-Attribut der Annotation ist EnumValue > –

3

Meine Erklärung auf @ assylias Lösung:

Was wollen wir über die Art der Klasse zum Ausdruck bringen, dass es ein

Class<E>, for some E, that E <: Enum<E> 

ist aber Java uns nicht erlaubt, eine Variable vom Typ E in einführen ein Methodenkörper.

Normalerweise können wir Wildcard und Wildcard-Capture ausnutzen Variable eine versteckte Art

class G<T extends b(T)> { ... } // b(T) is a type expression that may contain T 

G<? extends A> --capture--> G<T>, for some T, that T <: A & b(T) 

einzuführen Aber das wird in unserem Fall nicht funktionieren, da T in Class<T> keine Schranke, die es Arbeit macht.

Also brauchen wir eine neue Art mit dem EnumClass<E>.enumClass() Wir nennen

class EnumClass<E extends Enum<E>> // called EnumValue in assylias's solution 

    EnumClass(Class<E> enumClass) 

    Class<E> enumClass() 

EnumClass<?> --capture--> EnumClass<E>, for some E, that E <: Enum<E> 

gebunden gewünschten einzuführen dann ein

Class<E>, for some E, that E <: Enum<E> 

zu erhalten, das ist das Ziel, das wir zu erreichen versucht haben. Wie können wir den Konstruktor von EnumClass aufrufen? Der Ursprung des Problems liegt darin, dass wir keinen richtigen Typ für enumClass haben, aber der Konstruktor EnumClass möchte eine richtig getippte enumClass.

Class<not-proper> enumClass = ...; 
new EnumClass<...>(enumClass); // wont work 

Zum Glück (?) Die rohe Art hilft hier die Generika

EnumClass raw = new EnumClass(enumClass); // no generics 
EnumClass<?> wild = raw; 

So sind die Mindest-Gymnastik wir die Klasse auf die gewünschte Art ausführen müssen, die Typprüfung deaktiviert zu werfen ist

((EnumClass<?>)new EnumClass(enumClass)).enumClass() 
+0

+1 Gute Erklärung. Ich frage mich, ob 'EnumClass wild = neue EnumClass <> (enumClass);' funktioniert in Java 7. –