3

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 required: RETURN_TYPE

消息的位置,如果就在呼叫的开始之后,在类型的“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");
    }
}

提前致谢,

尼科

4

3 回答 3

3

实际上,如果这是您的编译器中的错误,我不会感到惊讶。通过认真使用泛型(你正在做的事情,结合参数化方法、有界通配符和泛型的其他“高级”使用),去年我在 javac 中遇到了两三个问题(令人讨厌的是,同一个单元通常在 IDE 中编译得很好)。

在您的情况下,我相当确定这是一个错误,因为编译器抱怨的部分extractor.extractEnum是返回 aObject而不是 a RETURN_TYPE。而且不管它对你的枚举方法参数做了什么疯狂的推断......它从类型签名中知道Extractor<RETURN_TYPE>Extractor 是一个,所以你应该总是能够说return extractor.extractEnum(...);

该死的证据是,即使您使用参数调用该方法null(从而完全消除了参数中枚举泛型的任何潜在并发症),编译器仍然会抱怨。特别是,它现在说它认为 Extractor 的返回类型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);
  }
}

PS 通常使用单个大写字母来指定泛型类型参数。我不会说“我是对的,你错了”,但请记住,与使用 Extractor 相比,我发现您的代码更难阅读和遵循。(从 Hemal 对他的回答的措辞来看,这对他来说也是一样的。)

于 2009-10-09T16:39:27.207 回答
2

我无法推断出原来的问题。

我是否正确,Extractor.extract有两个类型参数,U必须是一个EnumT哪个是任意类型?在通用调用中,VV两者都是TU?如果UVV比参数必须是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
  }
}
于 2009-10-09T05:03:56.460 回答
1

看起来你Field.getType( )只会返回一个通用类。因为您将尝试使用已删除类型信息的类型,所以此函数将不得不发出"unchecked"警告,并且泛型接口上的所有类型信息都将被删除。

请记住,使用类型擦除,您的界面如下所示:

interface Extractor {
    public Object extractEnum( final Class enumType );
}

所以,因为所有类型信息都被删除了,返回类型extractEnumjava.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 >接口调用所期望的不同。

于 2009-10-09T14:43:28.787 回答