2013-03-12 13 views
10

Ho il seguente codice BeanValidation che funziona bene, e permette di convalidare che un fagiolo annotato con:Generici e Classe <? si estende Enum <?>>, EnumSet.allOf (classe) vs class.getEnumConstants()

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

    public enum MyTestEnum { 
    VAL1, VAL2; 
    } 

saranno convalidati solo se il valore del campo è "VAL1" o "VAL2".

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); 
    } 
    } 

} 

Quello che non capisco è il motivo per cui il mio primo tentativo non è riuscito. Utilizzando invece del enumSelected.getEnumConstants() sopra il seguente codice:

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

Intellij 12 non evidenzia alcun errore, ma il compilatore dice:

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<?>> 

non capisco il problema, e ho anche che il codice che funziona bene:

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

Biglietto correlato: http://stackoverflow.com/questions/5548091/problem-when-trying-to-use-generics –

+0

Anche questo: http://stackoverflow.com/questions/3546745/multiple-wildcards-on- a-generic-m ethods-makes-java-compiler-and-me-very-confu – assylias

+1

@ChristopheRoussy: biglietto? StackOverflow è un sistema di supporto ora :) – mellamokb

risposta

8

la mia ipotesi è che nel ? extends Enum<?> il due ? potrebbero essere diversi, mentre allOf prevede uno T extends Enum<T> in cui entrambi gli T sono uguali.

Ad esempio, si consideri il seguente codice:

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; } 
} 

Queste linee compilare:

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

perché sappiamo che i due T nel enumValue.enumClass() sono gli stessi, ma questo non lo farà:

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

perché hai perso le informazioni da utilizzando un Class<? extends Enum<?>> come passaggio intermedio.

+0

grazie, suppongo di non avere altra scelta perché non riesco a rendere questo attributo enum dell'annotazione come EnumValue > –

3

La mia spiegazione sulla soluzione di @ assylias:

Quello che vogliamo esprimere sul tipo di classe è che si tratta di un

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

ma Java non ci permette di introdurre un tipo di variabile E in un metodo di corpo.

Di solito, possiamo sfruttare jolly e jolly acquisizione di introdurre un tipo di nascosto variabile

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) 

Ma questo non funzionerà nel nostro caso, dal momento che in TClass<T> non hai limite che lo fa funzionare.

quindi abbiamo bisogno di introdurre un nuovo tipo con il desiderato legato

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> 

Abbiamo poi chiamiamo EnumClass<E>.enumClass() fino ad ottenere una

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

che è l'obiettivo che stiamo cercando di realizzare.

Ma come possiamo chiamare il costruttore di EnumClass? L'origine del problema è che non abbiamo un tipo corretto per enumClass, tuttavia il costruttore di EnumClass vuole un enumClass correttamente digitato.

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

fortuna (?) Il tipo grezzo aiuta qui che disabilita i generici controllo di tipo

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

Così la ginnastica minimi di cui abbiamo bisogno per eseguire per lanciare la classe al tipo desiderato è

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

+1 Buona spiegazione. Mi chiedo se 'EnumClass wild = new EnumClass <> (enumClass);' funzioni in Java 7. –

Problemi correlati