Java 泛型:javac 和 Eclipse 编译谁是对的?

问题描述 投票:0回答:3

调用该方法:

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

java eclipse javac
3个回答
5
投票

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
  }

2
投票

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

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

0
投票

两条评论:

a.正如您在评论之一中提到的,something() 签名中的 TG 类型参数根本没有必要,因为您在方法中执行的子类 TG 没有任何特定内容。

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

public @Nonnull Maybe<Foo> visit() {
    final Foo bar = new BarExtendsFoo();
    return Maybe.something(bar);
}
© www.soinside.com 2019 - 2024. All rights reserved.