为什么局部变量会影响类型推断等式约束?

4

我在理解类型推断中的捕获时遇到了问题。我有一些代码看起来像这样:

import java.util.EnumSet;

class A {
    static enum E1 {
    X
    }

    private static <T extends Enum<T>> EnumSet<T> barEnum(Class<T> x) {
        return null;
    }

    private static void foo1(EnumSet<E1> s, E1 e) {
        EnumSet<E1> x2 = barEnum(e.getClass());
    }

    private static void foo2(EnumSet<E1> s) {
        EnumSet<E1> x = barEnum(s.iterator().next().getClass());
    }
}

编译时会出现两个错误:

Test.java:15: error: method barEnum in class A cannot be applied to given types;
        EnumSet<E1> x2 = barEnum(e.getClass());
                         ^
  required: Class<T>
  found: Class<CAP#1>
  reason: inference variable T has incompatible equality constraints E1,CAP#2
  where T is a type-variable:
    T extends Enum<T> declared in method <T>barEnum(Class<T>)
  where CAP#1,CAP#2 are fresh type-variables:
    CAP#1 extends E1 from capture of ? extends E1
    CAP#2 extends E1 from capture of ? extends E1
Test.java:19: error: method barEnum in class A cannot be applied to given types;
        EnumSet<E1> x = barEnum(s.iterator().next().getClass());
                        ^
  required: Class<T>
  found: Class<CAP#1>
  reason: inference variable T has incompatible equality constraints E1,CAP#2
  where T is a type-variable:
    T extends Enum<T> declared in method <T>barEnum(Class<T>)
  where CAP#1,CAP#2 are fresh type-variables:
    CAP#1 extends E1 from capture of ? extends E1
    CAP#2 extends E1 from capture of ? extends E1
Note: Test.java uses unchecked or unsafe operations.
Note: Recompile with -Xlint:unchecked for details.
2 errors

在尝试理解错误时,我将foo2更改为捕获getClass()的值,并将其存储在本地变量中,以查看实际类型:

private static void foo2(EnumSet<E1> s) {
    // this works
    Class<? extends Enum> c = s.iterator().next().getClass();
    EnumSet<E1> y = barEnum(c);
}

现在,错误已经消失,代码已编译。我不明白引入一个与表达式完全相同的本地变量如何改变类型推断算法并解决问题。


它与本地变量无关,而与相关类型有关。 - user207421
@EJP 能详细说明吗?局部变量不会改变类型,为什么会有差异?泛型版本为什么能够工作? - Jens
2个回答

1
当您将 s.iterator().next().getClass() 分配给本地变量时,您正在使用原始类型 - Enum。这是您如何解决编译错误但获得警告的方式。
如果您使用强制转换而不是本地变量,则可以获得相同的行为:
private static void foo2(EnumSet<E1> s) {
    EnumSet<E1> x = barEnum((Class<? extends Enum>)s.iterator().next().getClass());
}

您可以使用以下方法避免强制转换:

private static void foo2(EnumSet<E1> s) {
    EnumSet<E1> x = barEnum(s.iterator().next().getDeclaringClass());
}

谢谢。本地变量应该具有类型Class <? extends E1>,这样就是一致的。但我仍然不明白为什么泛型函数可以在没有强制转换的情况下编译。 - Jens
我想我会把关于通用性的问题拆开成另一个问题,并接受您的答案。 - Jens

-1
在您的原始示例中,TE1 不兼容,因为 T 是一个枚举类型而 E1 不是。 (仍然不知道原因。欢迎提供建议。)
更改您的方法声明以包括类型定义,告诉编译器 E1 也是一个枚举类型:
private static <E1 extends Enum<E1>> void foo1(EnumSet<E1> s, E1 e) {

我不确定我理解了。E1enum E1 {X},这意味着它扩展了 Enum<E1>(JLS:名为 E 的枚举类型的直接超类是 Enum<E>)。将其泛型化会导致与 foo3 基本相同的功能,但我仍然不明白为什么这会有所不同。 - Jens

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