2017-02-22 175 views
2

全部, 要测试我在下面创建的示例代码的Java泛型类型推断。Java泛型类型推断混淆

public static <U> U addBox2(U u1, U u2) { 
    return u2; 
} 

static interface A {} 
static interface B {} 
static class S {} 
static class C extends S implements A,B {} 
static class D extends S implements B,A {} 

如果你看到上面的A和S没有任何关系。

在下面的代码 -

A a = addBox2(new C(),new D()); 

我希望能收到编译错误,因为推断出的类型是S和我将其分配给A和A和S有没有关系还是这只是正常工作。

有人可以帮我解释为什么这种行为?

+2

A和S没有关系,但既然你将C和D传递给方法,并实现A(和B)他们有一些共同点,即它们都是As。除此之外,您传递的所有内容都将是一个“对象”,并且这将是最不常见的分母。 – Thomas

+0

你为什么认为它会输入'S'?为什么你忽略实现的接口'A'和'B'? – Tom

+0

我使用IntelliJ IDEA进行开发,IDEA向我显示T被推断为S.可能是IntelliJ向我展示了错误的东西。 – user3616964

回答

0

类型参数Ua变量的类型推断出来,您将addBox2(...)的结果分配给您的情况,这在您的情况下为A。因为CD实施A它完美地工作。

当然,以下将不起作用:

S s = addBox2(new C(),new D()); 
A a = s; // compile error, type mismatch 
+0

我使用IntelliJ IDEA进行开发,IDEA显示我T被推断为S.可能是IntelliJ向我展示了错误的东西。 – user3616964

1

按照要求:

AS没有关系的确但因为你传递一个CD的方法和两实施A(和B)他们有一些共同点,即他们都是A s。

这意味着以下将工作:

A a = addBox2(new C(),new D()); //common type is A 
B b = addBox2(new C(),new D()); //common type is B 
S s = addBox2(new C(),new D()); //common type is S 
Object o = addBox2(new C(),new D()); //common type is Object 

只要类型推断就可以解决从分配以及它应该工作参数的泛型类型(注意,类型推断是不那么好Java之前的版本,特别是5和6)。

你可以,但是,通过将其传递给方法调用自己定义的类型:

A a = EnclosingClass.<S>addBox2(new C(),new D()); //static method within EnclosingClass 
A a = this.<S>addBox2(new C(),new D()); //instance method 

在这两种情况下,你定义泛型类型为S,因此分配将无法正常工作。

0

我怀疑Eclipse和IntelliJ没有正确注释这些。有了这个(略有改动)例如:

public static <U> U getU(U u1, U u2) { 
    return u2; 
} 

static interface A {} 
static class S {} 
static class C extends S implements A {} 
static class D extends S implements A {} 
static class T implements A {} 

public static void main(String[] args) { 
    A a = getU(new C(), new D()); 
    A b = getU(new C(), new T()); 
} 

我看到下面的时候我鼠标悬停在main()第一次调用getU()

<? extends S> ? extends S analysis.Delme.getU(? extends S u1, ? extends S u2)

这显然是不正确 - A不延伸S。我怀疑Eclipse在这里错误地将泛型简单化了,宁愿报告CD都从S延伸,而不是两者都实现A

如果我将鼠标悬停在第二个电话我,而不是得到合理得多:

<A> A analysis.Delme.getU(A u1, A u2)

这大概是编译器是什么实际上在这里做了两个电话。


注为Thomas指出,即使两个类没有任何共同之处(如ST),他们仍然Object延长,所以Object c = getU(new S(), new T());是有效的。

0

不要相信编辑器的工具提示可能无法应对复杂的通用结构。推断类型为S & A & B(在Java 8之前),可以将其分配给A。在Java 8中,推断类型仅为A,因为泛型方法调用是所谓的多表达式使用目标类型。对于您的addBox调用,它没有区别。

为了说明这个问题,你可以使用Java 7的兼容模式写

Object o = Arrays.asList(new C(), new D()); 
在Eclipse

,并且将鼠标悬停在asList。这将打印
<? extends S> List<? extends S> java.util.Arrays.asList(? extends S... a),类似于addBox调用你的问题,但是当你的代码更改为

List<A> list = Arrays.asList(new C(), new D()); 

你的编译器错误“类型不匹配:不能从List<S&A&B>转换为List<A>”(仍然在Java中7模式),显示实际推断的类型是S & A & B,而不是? extends S

当您将语言遵从性级别转换为Java 8时,编译器错误将消失,如在Java 8中,目标类型将用于推断类型,这将是A而不是S & A & B

至于说,你addBox调用,这都没有区别AS & A & B是否被推断,既可以分配给A,所以它的工作原理下两者的Java 7和Java 8合规水平。但是当你使用一个调用,它在那里有所作为,就像Arrays.asList一样,你可以看到实际推断的类型,从不是? extends S作为编辑器的工具提示声明...