java中的枚举和泛型方法

时间:2009-10-09 01:33:25

标签: java generics enums crtp

我仍然遇到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");
    }
}

提前感谢,

尼科

3 个答案:

答案 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必须是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
  }
}

答案 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 >不同。