泛型和Class<? extends Enum<?>>,EnumSet.allOf(class)与class.getEnumConstants()的区别

15
我有以下的BeanValidation代码,它可以很好地工作,并且允许验证使用注释标记的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;
  }

相关票据:https://dev59.com/nVXTa4cB1Zd3GeqP5-WP - Christophe Roussy
这个也是:https://dev59.com/weo6XIcBkEYKwwoYTy4d - assylias
1
@ChristopheRoussy:票?现在StackOverflow成了一个支持系统吗 :) - mellamokb
@mellamokb 我同时使用Trac,当我想修复它时,5分钟的限制已经结束了 :P 是的,有时候它看起来像一个支持系统,如果你知道我的意思... - Christophe Roussy
2个回答

13

我猜在 ? extends Enum<?> 中的两个 ? 可能是不同的,而 allOf 期望一个 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());

因为我们知道enumValue.enumClass()中的两个T是相同的,但下面这种情况不是:

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

由于您使用了Class<? extends Enum<?>>作为中间步骤,导致信息丢失。


谢谢,我想我别无选择,因为我无法将注释的枚举属性设置为EnumValue <T extends Enum <T>>。 - Sebastien Lorber

4

我对 @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)

但是在我们的情况下,这样做行不通,因为Class<T>中的T没有绑定可以使其工作。
因此,我们需要引入一个具有所需绑定的新类型。
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>

我们随后调用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

幸运的是,原始类型可以帮助这里禁用泛型类型检查。

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

所以我们需要执行的最小操作来将类转换为所需类型是:
((EnumClass<?>)new EnumClass(enumClass)).enumClass()

很好的解释。我想知道在Java 7中是否可以使用EnumClass<?> wild = new EnumClass<>(enumClass); - Paul Bellora

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接