我仍然遇到java泛型系统中的一些极端情况。
我有这种方法(我只对签名感兴趣):
interface Extractor<RETURN_TYPE> {
public <U extends Enum<U>> RETURN_TYPE extractEnum(final Class<U> enumType);
}
(想想一个接口,其实现有时会提取一个EnumSet,有时一个实现提取一个JComboBox等。)
我想用运行时获得的类来调用它,所以我只是这样称呼它:
public static <RETURN_TYPE> RETURN_TYPE extractField(final Extractor<RETURN_TYPE> extractor, final Field field) {
final Class<?> type = field.getType();
if (type.isEnum())
return extractor.extractEnum(/* error here*/type.asSubclass(Enum.class));
throw new RuntimeException("the rest of the visitor is not necessary here");
}
我收到一条奇怪的错误消息: 不兼容的类型 发现:java.lang.Object 必需:RETURN_TYPE
消息的位置,如果恰好在打开braket之后,在类型的“t”之前。
如果我从非通用上下文中调用它,它可以工作:
Integer extractField(final Extractor<Integer> extractor, final Field field) {
final Class<?> type = field.getType();
if (type.isEnum())
return extractor.extractEnum(type.asSubclass(Enum.class));
throw new RuntimeException("the rest of the visitor is not necessary here");
}
请问有人解释并解决这个问题吗?
这是一个完整的文件,供想要玩它的人使用:
public class Blah {
interface Extractor<RETURN_TYPE> {
public <U extends Enum<U>> RETURN_TYPE extractEnum(final Class<U> enumType);
}
public static <RETURN_TYPE> RETURN_TYPE extractField(final Extractor<RETURN_TYPE> extractor, final Field field) {
final Class<?> type = field.getType();
if (type.isEnum())
return extractor.extractEnum(/* error here*/type.asSubclass(Enum.class));
throw new RuntimeException("the rest of the visitor is not necessary here");
}
public static Integer extractField(final Extractor<Integer> extractor, final Field field) {
final Class<?> type = field.getType();
if (type.isEnum())
return extractor.extractEnum(type.asSubclass(Enum.class));
throw new RuntimeException("the rest of the visitor is not necessary here");
}
}
提前感谢,
尼科
答案 0 :(得分:3)
如果这是编译器中的错误,我不会感到惊讶。通过认真使用泛型(你正在做的事情,结合参数化方法,有界通配符和泛型的其他“高级”用法)我去年在javac遇到了两三个问题(烦人的,同一个单位经常在IDE中编译好。)
在你的情况下,我很确定这是一个错误,因为编译器抱怨的部分是extractor.extractEnum
正在返回Object
而不是RETURN_TYPE
。而且无论它对你的enum方法参数做了什么疯狂的推断......它的知道来自提取器是Extractor<RETURN_TYPE>
的类型签名,所以你应该总是能够说return extractor.extractEnum(...);
。
该死的证据是,即使您使用null
参数调用该方法(从而完全消除了参数中枚举泛型的任何潜在并发症),编译器仍会抱怨。特别是,它现在说它认为提取器的返回类型是U<RETURN_TYPE>
,这显然是垃圾。
一般来说,解决这些问题的方法是投入一些明确的演员表。如果将extractEnum的输出强制转换为RETURN_TYPE,编译器是否满意? 修改:不,实际上并非如此 - 它抱怨U<RETURN_TYPE>
和RETURN_TYPE
无法转换 - eep ......
如果您使用的是最新的1.6编译器,我建议您向Sun报告此问题,因为这对javac来说是一个非常大的问题。这是一个非常简短的测试用例,它可以用来运行它:
public class Test {
interface Sub<O> {
public <I extends Enum<I>> O method(final Class<I> enumType);
}
public static <O> O go(final Sub<O> sub) {
return sub.method(null);
}
}
P.S。通常使用单个大写字母来指定泛型类型参数。我不会说“我是对的,你错了”,但请记住,我发现你的代码比你使用Extractor更难阅读和遵循。 (根据Hemal对他的回答的判断来判断,对他来说也是如此。)
答案 1 :(得分:2)
我无法推断原始问题。
我是否更正Extractor.extract
有两个类型参数,U
必须是Enum
和T
这是一个任意类型?在通用调用中,VV
同时是T
和U
?如果U
为VV
,则参数必须为Class<VV>
,而不是Class<Enum>
。以下内容为我编译,但正如您所见,泛型方法需要提供Class<VV>
的实例
class Outer {
static class Extractor<T> {
public <U extends Enum<U>> T extract(final Class<U> lala) {
return null;
}
// two type parameters, T and U
// U must be an enum
// T is arbitrary class
}
static <VV extends Enum<VV>> VV extract(final Extractor<VV> extractor, Class<VV> vvClass) {
final Class<?> type = null;
return extractor.extract(vvClass);
// Outer.extract returns VV
// T -> VV
// it seems VV is also U
}
}
答案 2 :(得分:1)
看起来您的Field.getType( )
只会返回一个通用类。因为您将尝试放置已删除类型信息的类型,此函数必须发出"unchecked"
警告,并且通用接口上的所有类型信息将被删除。
请记住,对于类型擦除,您的界面如下所示:
interface Extractor {
public Object extractEnum( final Class enumType );
}
因此,由于所有类型信息都被删除,extractEnum
的返回类型为java.lang.Object
,因此您必须添加特定的强制转换。这正是你所得到的错误信息。
以下是代码的修改示例。
@SuppressWarnings( "unchecked" )
public static <RETURN_TYPE> RETURN_TYPE extractField(
final Extractor<RETURN_TYPE> extractor,
final Field field
)
{
final Class type = field.getType(); // unchecked
if (type.isEnum())
{
// needs cast
return (RETURN_TYPE) extractor.extractEnum( type ); // unchecked
}
throw new RuntimeException("the rest of the visitor is not necessary here");
}
不赞成此评论:回答有关编译错误原因的原始问题。这是一个方形钉成圆孔的问题。 type.asSubclass( Enum.class )
返回Class< ? extends Enum >
,它仍然与接口调用所期望的Class< U >
不同。