Java 8 需要强制转换,而 Java 7 不需要 - enum.getClass/getDeclaringClass

2022-09-02 13:36:36

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

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.
    }

}

错误显示为:

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

这是 Java 8 的 bug 还是新功能?


答案 1

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

首先,让我们澄清一下您的示例。Object.getClass 的返回类型是特殊的:

实际结果类型是 where 是擦除所调用的表达式的静态类型。Class<? extends |X|>|X|getClass

在本例中,将是 type 参数 ,它将擦除到 。所以返回.EnumSet.allOf 声明类型参数 ,在你的例子中被推断为其类型参数。XCEnumsampleCol.getClass()Class<? extends Enum>E extends Enum<E>? extends Enum

重要的部分是原始类型。原始类型的使用已经被看到来消除看似不相关的泛型,例如在这篇文章中:为什么这个泛型java代码无法编译?在他的回答中,Jon Skeet引用了JLS §4.8(“原始类型”)来涵盖这种不直观的行为。Enum

在 Java 7 的示例中似乎也发生了类似的行为:允许使用“未选中的调用”警告进行编译(这将被随后的“未选中的转换”警告所隐藏,该警告将生成的 raw 分配给 )。EnumSet.allOf(sampleCol.getClass())EnumSetSet<C>

问题就变成了:在泛型通配符的边界中出现原始类型是否允许未经检查的转换?JLS §4.8没有提到这一点,所以它是模棱两可的。这可能是一个错误,但它似乎是对这种行为的合理收紧。虽然像标准原始类型这样的原始类型本身可能来自遗留 API,但像“半生不熟”的类型只能发生在泛型之后,因此让它中断泛型类型检查并没有真正意义。EnumClass<? extends Enum>

无论如何,我有兴趣看看是否有人可以指出有关此更改的文档 - 我的搜索没有发现任何内容。


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


答案 2

推荐