Generics and Class<? extends Enum<?>>, EnumSet.allOf(class) vs class.getEnumConstants()

后端 未结 2 1383
無奈伤痛
無奈伤痛 2021-02-19 13:11

I have the following BeanValidation code that works fine, and permits to validate that a bean annotated with:

  @EnumValue(enumClass = MyTestEnum.class)
  privat         


        
相关标签:
2条回答
  • 2021-02-19 13:49

    My guess is that in ? extends Enum<?> the two ? could be different whereas allOf expects a T extends Enum<T> where both T are the same.

    For example, consider the following 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; }
    }
    

    These lines will compile:

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

    because we know that the two T in enumValue.enumClass() are the same but this won't:

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

    because you have lost information by using a Class<? extends Enum<?>> as an intermediate step.

    0 讨论(0)
  • 2021-02-19 13:55

    My explanation on @assylias's solution:

    What we want to express about the type of the class is that it's a

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

    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

    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)
    

    But this won't work in our case, since T in Class<T> does not have a bound that makes it work.

    So we need to introduce a new type with the desired bound

    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>
    

    We then call EnumClass<E>.enumClass() to yield a

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

    which 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 for enumClass, yet the constructor of EnumClass wants a properly typed enumClass.

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

    Fortunately(?) the raw type helps here which disables generics type checking

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

    So the minimum gymnastics we need to perform to cast the class to the desired type is

    ((EnumClass<?>)new EnumClass(enumClass)).enumClass()
    
    0 讨论(0)
提交回复
热议问题