简体   繁体   English

Java泛型:Javac或Eclipse编译谁正确?

[英]Java Generics: Who is right, javac or Eclipse compile?

Calling this method: 调用此方法:

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

like this: 像这样:

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

compiles just fine in Eclipse, but javac gives an "incompatable types" warning: 在Eclipse中可以正常编译,但是javac给出了“不兼容类型”警告:

found   : BarExtendsFoo

required: Foo 需要:Foo

There are obviously some differences between javac and Eclipse. javac和Eclipse之间显然存在一些差异。 However, the main point here is that javac is correct in emitting the error. 但是,这里的要点是javac在发出错误时是正确的。 Ultimately, your code converts a Maybe<BarExtendsFoo> to a Maybe<Foo> which is risky. 最终,您的代码将Maybe <BarExtendsFoo>转换为具有风险的Maybe <Foo>。

Here's a rewrite of the visit() method: 这是对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;      
  }

This rewrite is practically identical to your code but it explicitly shows the assignment you're trying to make from Maybe<BarExtendsFoo> to Maybe<Foo>. 该重写实际上与您的代码相同,但是它明确显示了您要进行的从Maybe <BarExtendsFoo>到Maybe <Foo>的分配。 This is risky. 这是有风险的。 Indeed my Eclipse compiler issues an error on the assignment line. 确实,我的Eclipse编译器在分配行上发出错误。 Here's a piece of code that exploits this risk to store an Integer inside a Maybe<String> object: 这是一段利用这种风险将整数存储在Maybe <String>对象中的代码:

  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
  }

I do not understand why javac did not infer the correct type, 我不明白为什么javac没有推断出正确的类型,
but you can help the compiler by supplying the types as in 但是您可以通过提供如下所示的类型来帮助编译器

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

Two comments: 两条评论:

a. 一种。 As you mentioned in one of your comments, The TG type parameter in something()'s signature is not necessary at all, as there is nothing specific to the subclass TG that you're doing in the method. 正如您在评论中提到的那样,根本没有必要在something()签名中使用TG类型参数,因为在该方法中对子类TG没有特定的定义。

b. The easiest soluion to this would be to help the compiler to understand which type you're using by explicitly assigning the newly created object to a variable (which is usually good practice anyway). 最简单的解决方案是通过将新创建的对象显式分配给变量来帮助编译器了解您正在使用的类型(无论如何通常都是很好的做法)。 It's now clearer to both the compiler and human readers how you'd like to call the method: 现在,对于编译器和普通读者来说,您如何调用该方法都更加清楚:

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

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM