泛型方法执行隐式转换而非泛型方法需要显式转换
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
。
这个问题是相关的
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 whichgetClass
is called.
这意味着在您的通用方法中,barEnum
是使用 Class<? extends Enum>
而不是 Class<? extends Enum<E>>
调用的。由于 Enum
是原始类型,这会创建一个未经检查的调用,这反过来意味着 return 类型被删除(另请参阅:
所以在那种情况下 barEnum
实际上是 returning 一个 EnumSet
,它是一个原始类型,可以未经检查地转换为 EnumSet<E>
.
对于您的非泛型方法,您必须将参数显式转换为 Class<E1>
,因此没有未经检查的方法参数转换,因此没有未经检查的调用。但是,这也意味着 return 类型不会被删除,编译器找不到对该调用有效的 T
。