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

时间:2018-01-31 09:11:09

标签: java generics type-inference

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

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);
}

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

2 个答案:

答案 0 :(得分: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());
}

答案 1 :(得分:-1)

在原始示例中,TE1不兼容,因为TenumE1不是。{0}}。 (但仍然不知道为什么。建议欢迎。)

更改方法声明以包含类型定义,以告诉编译器E1也是enum

private static <E1 extends Enum<E1>> void foo1(EnumSet<E1> s, E1 e) {