【问题标题】:Java Generics: Who is right, javac or Eclipse compile?Java 泛型:谁是对的,javac 还是 Eclipse 编译?
【发布时间】:2010-01-11 09:23:52
【问题描述】:

调用这个方法:

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

【问题讨论】:

  • 一个没有非标准注释和缺少类的工作示例将使人们更容易提供帮助。
  • 我有一个简单的问题...编译器如何知道您要在调用中返回的实际类型:Maybe.something(new BarExtendsFoo())
  • 为什么不公开@Nonnull Maybe 访问() { ...} ?
  • dribeas,从visit()的签名中应该就知道了,里面明确说明了想要的类型。

标签: java eclipse compiler-construction javac


【解决方案1】:

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

这里是 visit() 方法的重写:

  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 到 Maybe 进行的分配。这是有风险的。事实上,我的 Eclipse 编译器在赋值行上发出错误。下面是利用这种风险将 Integer 存储在 Maybe 对象中的一段代码:

  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
  }

【讨论】:

  • 从 Maybe 到 Maybe 的转换不是我所期望的。 something() 方法应该返回Maybe&lt;TV&gt;,所以TV 应该是Foo 而不是BarExtendsFoo。重写仅对 javac 有效(相同)。 Eclipse 接缝使用预期的返回值来推断(正确的?)TV 类型,以便 something() 创建一个 Maybe
【解决方案2】:

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

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

【讨论】:

  • 这确实解决了这个问题,尽管我希望避免这种显式输入,而使用 some() 方法的相当复杂的签名。我现在已将其简化为此,因为无论如何我都需要提供类型: Maybe something(final TV value) 感谢所有这些提示。
【解决方案3】:

两个cmets:

一个。正如您在其中一个 cmets 中提到的,something() 签名中的 TG 类型参数根本不需要,因为您在方法中所做的子类 TG 没有任何特定内容。

b.最简单的解决方案是通过将新创建的对象显式分配给变量来帮助编译器了解您使用的类型(这通常是一种很好的做法)。现在,编译器和人类读者都更清楚您希望如何调用该方法:

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

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-12-15
    • 1970-01-01
    • 2011-02-20
    • 2018-01-29
    • 2012-09-24
    • 1970-01-01
    相关资源
    最近更新 更多