Java 8需要进行转换,而Java 7则不需要--enum.getClass/getDeclaringClass

Old*_*eon 19 java generics java-8

我意识到Java 8仍然处于测试阶段,但这个让我感到奇怪:

public class Fields<C extends Enum<C>> {

    public Fields(Set<C> columns) {
        // A sample column used to find the universe of the enum of Columns.
        C sampleCol = columns.iterator().next();
        // Java 8 needs a cast here.
        Set<C> allColumns = EnumSet.allOf((/*Class<C>)*/ sampleCol.getClass());
        // ... there's more to this that I've deleted.
    }

}
Run Code Online (Sandbox Code Playgroud)

错误如下:

error: incompatible types: inferred type does not conform to equality constraint(s)
            Set<C> allColumns = EnumSet.allOf(sampleCol.getClass());
    inferred: C
    equality constraints(s): C,CAP#1
  where C is a type-variable:
    C extends Enum<C> declared in class Test.Fields
  where CAP#1 is a fresh type-variable:
    CAP#1 extends Enum from capture of ? extends Enum
Run Code Online (Sandbox Code Playgroud)

这是Java 8的错误还是新功能?

Pau*_*ora 18

有趣的是,这是原始类型处理的微妙变化.

首先,让我们澄清你的例子.返回类型Object.getClass是特殊的:

实际结果的类型是Class<? extends |X|>其中|X|是静态类型上其表达的擦除getClass被调用.

在这种情况下,X将是类型参数C,其擦除Enum.所以sampleCol.getClass()回报Class<? extends Enum>.EnumSet.allOf声明type参数E extends Enum<E>,并在您的情况下? extends Enum被推断为其类型参数.

重要的是这Enum是一种原始类型.原始类型的使用已被视为删除看似无关的泛型,例如在这篇文章中:为什么这个通用的Java代码不能编译?在他的答案,乔恩斯基特援引JLS§4.8("原始类型")来弥补这一直观行为.

在使用Java 7的示例中似乎发生了类似的行为:EnumSet.allOf(sampleCol.getClass())允许使用"未经检查的调用"警告进行编译(这会被后续的"未经检查的转换"警告隐藏,从而将生成的原始内容分配EnumSetSet<C>).

问题变成:在通用通配符的边界中出现原始类型是否允许未经检查的转换?JLS§4.8没有提到这一点,因此它含糊不清.可能这是一个错误,但似乎合理收紧了这种行为.虽然标准的原始类型Enum本身可能是传统API所期望的,但是"半生不熟"类型Class<? extends Enum>只能在泛型后发生,因此让它破坏泛型类型检查并没有多大意义.

无论如何,我有兴趣看看是否有人可以指出有关此更改的文档 - 我的搜索没有改变任何东西.


关于您的特定代码:您应该使用getDeclaringClass().编译器无法知道调用getClassa C将完全返回Class<C>; 实际上,如果在具有常量特定类的枚举上使用它,则不会.这正是Enum声明该方法的用例.