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

时间:2018-01-31 10:19:32

标签: java generics type-inference unchecked-conversion

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

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

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

3 个答案:

答案 0 :(得分:2)

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

  

实际结果类型为Class<? extends |X|>,其中|X|是要调用getClass的表达式的静态类型的擦除。

这意味着在您的通用方法中,使用barEnum而不是Class<? extends Enum>调用Class<? extends Enum<E>>。由于Enum是原始类型,因此会创建一个未经检查的调用,这反过来意味着将删除返回类型(另请参阅:Why is generic of a return type erased when there is an unchecked conversion of a method parameter in Java 8?)。

因此,在这种情况下,barEnum实际上会返回EnumSet,这是一种可以未经检查转换为EnumSet<E>的原始类型。

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

答案 1 :(得分:0)

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

由于静态方法不会继承泛型类的类型参数,因此无法编译。然后,您尝试获取参数化类型的运行时类信息,这些信息不会起作用,因为Java使用擦除来实现其泛型。

一旦你超越了简单的集合等等,Java中的泛型并不容易。这就是常见问题解答是不可或缺的资源的原因。

答案 2 :(得分:0)

当我为获得的类提取变量并让我的IDE修复&#39;一切都可以,我明白了:

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

如你所见,aClass是未参数化的(Enum上有一条警告说它是“原始的”#)。此外,它声明它可以是Enum的任何子类,而barEnum只接受特定的枚举,并且因为特定的枚举是final类(它们不能有子类),所以存在冲突。< / p>

然后我将batEnum更改为

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

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