泛型方法执行隐式转换而非泛型方法需要显式转换

Generic method performs implicit cast while non-generic method needs explicit cast

这个问题是相关的 。可以通过添加强制转换来进行未经检查的转换来解决原始问题。所以现在我有以下代码:

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((Class<E1>)e.getClass());
    }

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

我的初衷是写一个泛型方法。所以我将方法 foo2() 概括为:

private static <E extends Enum<E>> void foo3(EnumSet<E> s) {
    EnumSet<E> x = barEnum(s.iterator().next().getClass());
}

这显然包含未经检查的转换并带有适当的警告。但我没有将 getClass() 的结果显式转换为 Class<E>。由于 foo1() 是通用方法 foo3() 的一个实例,我预计我也需要在此处添加强制转换。比较 foo1()foo4()...

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

...两者实际上相似(主要区别在于 foo1() 中的 E1 e 参数。然而 foo1() 编译,但 foo4() 不编译。我觉得这是不一致的。是否有任何规则允许对泛型方法进行隐式转换?

我的建议是阅读优秀的 Java Generics FAQ: http://www.angelikalanger.com/GenericsFAQ/JavaGenericsFAQ.html

我没想到,它不会编译,因为静态方法不继承泛型 class 的类型参数。然后,您试图获取参数化类型的运行时 class 信息,这将不起作用,因为 Java 对其泛型实现使用擦除。

Java 中的泛型一点也不简单,一旦你超越了简单的集合等。这就是为什么常见问题解答是不可或缺的资源。

当我为获得的 Class 提取一个变量并让我的 IDE 'fix' 尽其所能时,我得到这个:

private static void foo4(Iterable<E1> s) {
    Class<? extends E1> aClass = s.iterator().next().getClass();
    EnumSet<E1>         x      = barEnum(aClass);
}

如您所见,aClass 未参数化(Enum 上有一条警告说它是 'raw')。此外,它声明它可以是 Enum 的任何子类,而 barEnum 只接受特定的枚举,并且由于特定的枚举是 final 类 (它们不能有 sub类 ) 有冲突。

然后我把batEnum改成

private static <T extends Enum<T>> EnumSet<T> barEnum(Class<? extends T> x) {

错误消失了,因为现在它接受 Enum 的子 类。

发生了两件事。首先,如果您查看 getClass 的 javadoc,它会说:

The actual result type is Class<? extends |X|> where |X| is the erasure of the static type of the expression on which getClass is called.

这意味着在您的通用方法中,barEnum 是使用 Class<? extends Enum> 而不是 Class<? extends Enum<E>> 调用的。由于 Enum 是原始类型,这会创建一个未经检查的调用,这反过来意味着 return 类型被删除(另请参阅:)。

所以在那种情况下 barEnum 实际上是 returning 一个 EnumSet,它是一个原始类型,可以未经检查地转换为 EnumSet<E>.

对于您的非泛型方法,您必须将参数显式转换为 Class<E1>,因此没有未经检查的方法参数转换,因此没有未经检查的调用。但是,这也意味着 return 类型不会被删除,编译器找不到对该调用有效的 T