I have the following BeanValidation code that works fine, and permits to validate that a bean annotated with:
@EnumValue(enumClass = MyTestEnum.class)
private String field;
public enum MyTestEnum {
VAL1, VAL2;
}
Will be validated only if the field value is "VAL1" or "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);
}
}
}
What I don't understand is why my first attempt failed. Using instead of the enumSelected.getEnumConstants()
above the following code:
Set<? extends Enum<?>> enumInstances = EnumSet.allOf(enumSelected);
Intellij 12 doesn't highlight any error, but the compiler says:
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<?>>
I don't understand the problem, and I also have that code which works fine:
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;
}
My explanation on @assylias's solution:
What we want to express about the type of the class is that it's a
but Java does not allow us to introduce a type variable
E
in a method body.Usually, we can exploit wildcard and wildcard capture to introduce a hidden type variable
But this won't work in our case, since
T
inClass<T>
does not have a bound that makes it work.So we need to introduce a new type with the desired bound
We then call
EnumClass<E>.enumClass()
to yield awhich is the goal we've been trying to achieve.
But how can we call the constructor of
EnumClass
? The origin of the problem is that we don't have a proper type forenumClass
, yet the constructor ofEnumClass
wants a properly typedenumClass
.Fortunately(?) the raw type helps here which disables generics type checking
So the minimum gymnastics we need to perform to cast the class to the desired type is
My guess is that in
? extends Enum<?>
the two?
could be different whereasallOf
expects aT extends Enum<T>
where bothT
are the same.For example, consider the following code:
These lines will compile:
because we know that the two
T
inenumValue.enumClass()
are the same but this won't:because you have lost information by using a
Class<? extends Enum<?>>
as an intermediate step.