代码之家  ›  专栏  ›  技术社区  ›  tbk

Java泛型:谁是正确的,JavaC还是Eclipse编译?

  •  2
  • tbk  · 技术社区  · 16 年前

    调用此方法:

    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
    

    要求:

    3 回复  |  直到 15 年前
        1
  •  2
  •   user85421    16 年前

    我不明白为什么javac没有推断出正确的类型,
    但是你可以 帮助 编译器通过提供

    public @Nonnull Maybe<Foo> visit() {
        return Maybe.<Foo, BarExtendsFoo>something(new BarExtendsFoo());
    }
    
        2
  •  5
  •   Itay Maman    16 年前

    显然,javac和eclipse之间存在一些差异。然而,这里的要点是javac发出错误是正确的。最终,您的代码将一个可能的<barextendsfoo>转换为一个可能的<foo>,这是有风险的。

    下面是对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;      
      }
    

    此重写实际上与您的代码相同,但它显式地显示了您试图进行的分配,从可能的<barextendsfoo>到可能的<foo>。这是有风险的。实际上,我的Eclipse编译器在分配行上发出一个错误。下面是一段代码,它利用此风险将整数存储在可能的字符串对象中:

      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
      }
    
        3
  •  0
  •   RAY    15 年前

    两个评论:

    a.正如您在其中一条注释中提到的,something()签名中的tg type参数根本不需要,因为您在方法中所做的子类tg没有特定的内容。

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

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