对通用扩展存在的Enum类型感到困惑

时间:2014-12-25 17:43:05

标签: java generics interface enums type-erasure

问题1 :我对这些代码感到有点困惑:

public class MyClass1 <E extends Enum<?>> {
    // ...
}
public class MyClass2 <E extends Enum<E>> {
    // ... 
}

MyClass1MyClass2之间的区别是什么?三种不同的E是什么意思?

问题2 :来自Class Enum <E extends Enum<E>>

  

这是所有Java语言枚举类型的通用基类。

但是不是所有枚举类型Enum<E>的公共基类?

问题3 :现在我有这个课程:

public class MyClass<E extends Enum<E>> {

    // for example, EnumSet<Category> 
    public Set<E> category; 

    // how to initial this member
    private Class<E> enumType; 

    // initial this.category with given strings
    public void getEnumSetFromStringList(List<String> list) {
        this.category = EnumSet.noneOf(enumType);
        for (String str : list) {
            this.category.add(Enum.valueOf(this.enumType, str));
        }
    }
}

// this is the Category of Enum type
public enum Category {

    LOCATION("LOCATION"), 
    HUMAN("HUMAN"),
    // ...
    DESCRIPTION("DESCRIPTION");

    private final String categoryName;

    Category(String categoryName) {
        this.categoryName= categoryName;
    }
}
  1. 如何在enumType中初始化数据字段MyClass

  2. 如果MyClass不包含数据字段enumType,那么函数getEnumSetFromStringList(List<String> list)如何获得泛型<E extends Enum<?>>的类型?

  3. 我可以通过这种方式获取Enum类型:E.class?如果没有,是否在编译期间从泛型类型的 type-erasure 开始?

2 个答案:

答案 0 :(得分:4)

Enum<E extends Enum<E>>很难理解。

我们考虑两个enums

enum Colour { RED, GREEN, BLUE }

enum Shape { SQUARE, CIRCLE, TRIANGLE }

考虑compareTo()中的Enum方法。这用于根据声明的顺序比较同一Enum中的不同常量。如果Enum不是泛型类,则此方法的签名必须为int compareTo(Enum e) 。但这没有任何意义,因为那时你可以将ColourShape进行比较。我们希望compareTo中的Colour签名为int compareTo(Colour colour)。唯一的方法是Enum是具有类型参数E的通用类,而Colour的类型参数是Colour!只要Colour的类型参数为ColourShape的类型参数为Shape,我们就只能将ColourColour进行比较}和ShapeShape s。

所以在Enum的定义中,我们想要一些表达方式,对于每个子类,type参数不仅必须是enum,而且type参数应该是子类本身。因此,E不应仅扩展EnumE应扩展Enum<E>

这是Enum<E extends Enum<E>>来自的地方。

(你不应该写Enum<E extends Enum<?>>)。

将值赋给enumType的一种方法是将Class<E>传递给构造函数。喜欢这个

class MyClass2<E extends Enum<E>> {
    private final Class<E> enumType;

    MyClass2(Class<E> enumType) {
        this.enumType = enumType;
    }
}

如果您不想这样做,另一种在运行时获取Class<E>对象的方法是使用Enum实例方法getDeclaringClass()。这要求您手头有一个E实例来调用该方法。在运行时获取所有枚举常量数组的一种方法是编写e.getDeclaringClass().getEnumConstants()

你不能写E.class,因为你正确地说,泛型是使用类型擦除过程实现的。在运行时,ArrayList<String>只是ArrayList,因此无法访问类型参数。

答案 1 :(得分:3)

  

MyClass1和MyClass2有什么不同,这三种不同的E是什么意思?

MyClass1引入了一个名为E的泛型类型参数,它扩展了Enum<?>类。在这里,?意味着我们不知道(并且关心)Enum类的通用版本E进行扩展,因此通配符是使用

同时,MyClass2还引入了一个名为E的泛型类型参数,该参数再次扩展了Enum类型。这一次,Enum类型是相同 E的通用类型,这意味着此类型参数必须是Enum类的子类,并且还支持Enum中定义的所有方法,这些方法使用完全相同的类型E。例如,此类方法为getDeclaringClass(),将返回Class<E>而不是Class<Object>


  

但是不是所有枚举类型枚举的公共基类吗?

没有。如果它不是Enum<E>的子类,那么泛型参数将没有意义。


  

如何在MyClass中初始化数据字段enumType?

您必须在构造函数中传递Class<E>并将值复制到私有成员:

public MyClass(Class<E> enumType) {
    this.enumType = enumType;
}

  

如果MyClass不包含数据字段enumType,那么函数getEnumSetFromStringList(...)如何获得泛型类型?

由于类型擦除,它将无法获得它。如果您需要泛型类的类,则必须维护一个包含Class<E>值的成员;


  

我可以这样得到Enum类型:E.class?如果不是,那么在编译期间从泛型类型擦除的原因是什么?

你不能因为E只是运行时特定的别名。