Java Generics:谁是对的,javac还是Eclipse编译?

时间:2010-01-11 09:23:52

标签: java eclipse compiler-construction javac

调用此方法:

public static @Nonnull <TV, TG extends TV> Maybe<TV> something(final @Nonnull TG value) {
    return new Maybe<TV>(value);
}
像这样:

public @Nonnull Maybe<Foo> visit() {
    return Maybe.something(new BarExtendsFoo());
}

在Eclipse中编译得很好,但是javac给出了“不兼容的类型”警告:

found   : BarExtendsFoo

必需:Foo

3 个答案:

答案 0 :(得分:5)

javac和Eclipse之间显然存在一些差异。但是,这里的要点是javac在发出错误时是正确的。最终,您的代码会转换一个Maybe&lt; BarExtendsFoo&gt;到一个可能&lt; Foo&gt;这是有风险的。

这是access()方法的重写:

  public static <TV, TG extends TV> Maybe<TV> something(final TG value) {
     return new Maybe<TV>(value);
  }

  public static class Foo { }

  public static class BarExtendsFoo extends Foo { }

  public Maybe<Foo> visit() {
     Maybe<BarExtendsFoo> maybeBar = something(new BarExtendsFoo());
     Maybe<Foo> maybeFoo = maybeBar;  // <-- Compiler error here

     return maybeFoo;      
  }

此重写几乎与您的代码完全相同,但它明确地显示了您尝试从Maybe&lt; BarExtendsFoo&gt;中进行的分配。可能&lt; Foo&gt;。这是有风险的。实际上,我的Eclipse编译器在赋值行上发出错误。这是利用此风险将一个Integer存储在Maybe&lt; String&gt;内的一段代码。对象:

  public static void bomb() {
     Maybe<String> maybeString = new Maybe<String>("");

     // Use casts to make the compiler OK the assignment
     Maybe<Object> maybeObject = (Maybe<Object>) ((Object) maybeString); 
     maybeObject.set(new Integer(5));

     String s = maybeString.get(); // Runtime error (classCastException):
                                   //   java.lang.Integer incompatible with  
                                   //   java.lang.String
  }

答案 1 :(得分:2)

我不明白为什么javac没有推断出正确的类型,
但您可以通过提供

中的类型来帮助编译器
public @Nonnull Maybe<Foo> visit() {
    return Maybe.<Foo, BarExtendsFoo>something(new BarExtendsFoo());
}

答案 2 :(得分:0)

两条评论:

一个。正如您在其中一条评论中提到的那样,some()签名中的TG类型参数根本不是必需的,因为您在方法中没有特定于子类TG的内容。

湾对此最简单的解决方案是通过将新创建的对象显式地分配给变量来帮助编译器了解您正在使用的类型(这通常是很好的做法)。现在,对于编译器和人类读者来说,如何调用该方法更加清晰:

public @Nonnull Maybe<Foo> visit() {
    final Foo bar = new BarExtendsFoo();
    return Maybe.something(bar);
}