【问题标题】:Generic method performs implicit cast while non-generic method needs explicit cast泛型方法执行隐式转换,而非泛型方法需要显式转换
【发布时间】:2018-01-31 10:19:32
【问题描述】:

这个问题与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&lt;E&gt;。由于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 type-inference unchecked-conversion


    【解决方案1】:

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

    实际结果类型是Class&lt;? extends |X|&gt;,其中|X| 是调用getClass 的表达式的静态类型的擦除。

    这意味着在您的泛型方法中,barEnum 是使用 Class&lt;? extends Enum&gt; 而不是 Class&lt;? extends Enum&lt;E&gt;&gt; 调用的。由于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&lt;E&gt;

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

    【讨论】:

    • 不应该getClass()返回Class&lt;? extends E1&gt;吗?
    • @shmosel 是的,应该。但这不是我必须在非泛型方法中将演员表添加到Class&lt;E1&gt; 的原因吗,如上一段所述?
    • @Jens 有道理。您是否尝试将其分配给EnumSet&lt;? extends E1&gt;
    • @shmosel 我不确定你的意思?在foo3 getClass 中返回一个Class&lt;? extends Enum&gt; 并在foo4 中返回一个Class&lt;? extends E1&gt;
    • @Jens 我指的是foo4
    【解决方案2】:

    我的建议是阅读出色的 Java 泛型常见问题解答: http://www.angelikalanger.com/GenericsFAQ/JavaGenericsFAQ.html

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

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

    【讨论】:

      【解决方案3】:

      当我为获得的类提取变量并让我的 IDE '修复'它可以修复的所有内容时,我得到了这个:

      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 类(它们不能有子类),因此存在冲突。

      当我将batEnum 更改为

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

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

      【讨论】:

      • 因为动态链接,即使final 类在运行时也可以有子类。所以具体的枚举是final 应该无关紧要。这似乎是编译器无法推断出T 的问题(因为原始调用没有有效的T)。但是你的解决方案是正确的。
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2011-09-12
      • 2017-09-10
      相关资源
      最近更新 更多