Общие и класс <? extends Enum <? >>, EnumSet.allOf(класс) vs class.getEnumConstants()
У меня есть следующий код BeanValidation, который отлично работает и позволяет проверить, что a bean аннотируется с помощью
@EnumValue(enumClass = MyTestEnum.class)
private String field;
public enum MyTestEnum {
VAL1, VAL2;
}
Будет проверяться, только если значение поля "VAL1" или "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);
}
}
}
Я не понимаю, почему моя первая попытка не удалась. Используя вместо enumSelected.getEnumConstants()
выше следующий код:
Set<? extends Enum<?>> enumInstances = EnumSet.allOf(enumSelected);
Intellij 12 не выделяет никаких ошибок, но компилятор говорит:
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<?>>
Я не понимаю проблему, и у меня также есть код, который отлично работает:
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;
}
Ответы
Ответ 1
Я предполагаю, что в ? extends Enum<?>
два ?
могут быть разными, тогда как allOf
ожидает a T extends Enum<T>
, где оба T
совпадают.
Например, рассмотрим следующий код:
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; }
}
Эти строки будут скомпилированы:
EnumValue<?> enumValue = new EnumValue(MyEnum.class); // raw constructor
Set<? extends Enum<?>> enumInstances = EnumSet.allOf(enumValue.enumClass());
потому что мы знаем, что два T
в enumValue.enumClass()
совпадают, но это не будет:
EnumValue enumValue = new EnumValue(MyEnum.class);
Class<? extends Enum<?>> enumSelected = enumValue.enumClass();
Set<? extends Enum<?>> enumInstances = EnumSet.allOf(enumSelected);
поскольку вы потеряли информацию, используя Class<? extends Enum<?>>
в качестве промежуточного шага.
Ответ 2
Мои объяснения по решению @assylias:
Что мы хотим выразить о типе класса, так это то, что он
Class<E>, for some E, that E <: Enum<E>
но Java не позволяет вводить переменную типа E
в тело метода.
Обычно мы можем использовать подстановочный знак и подстановочный знак, чтобы ввести переменную скрытого типа
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)
Но это не будет работать в нашем случае, так как T
в Class<T>
не имеет привязки, которая заставляет его работать.
Итак, нам нужно ввести новый тип с требуемой границей
class EnumClass<E extends Enum<E>> // called EnumValue in assylias solution
EnumClass(Class<E> enumClass)
Class<E> enumClass()
EnumClass<?> --capture--> EnumClass<E>, for some E, that E <: Enum<E>
Затем мы вызываем EnumClass<E>.enumClass()
, чтобы получить
Class<E>, for some E, that E <: Enum<E>
которого мы пытаемся достичь.
Но как мы можем назвать конструктор EnumClass
? Происхождение проблемы состоит в том, что у нас нет подходящего типа для EnumClass
, но конструктор EnumClass
требует правильно напечатанного EnumClass
.
Class<not-proper> enumClass = ...;
new EnumClass<...>(enumClass); // wont work
К счастью (?) здесь используется тип raw, который отключает проверку типов generics
EnumClass raw = new EnumClass(enumClass); // no generics
EnumClass<?> wild = raw;
Итак, минимальная гимнастика, которую нам нужно выполнить, чтобы отличить класс от желаемого типа,
((EnumClass<?>)new EnumClass(enumClass)).enumClass()