全部, 为了测试我在下面创建的Java Generics Type Inference示例代码。
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没有任何关系,这仍然可以正常工作。
有人可以帮我解释一下这种行为的原因吗?
答案 0 :(得分:2)
根据要求:
A
和S
确实没有关系,但是因为您将C
和D
传递给该方法,并且都实现了A
(和B
)他们有一些共同点,即它们都是A
。
这意味着以下内容可行:
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
只要类型推断可以解决赋值中的泛型类型以及它应该工作的参数(请注意,在8之前的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
,因此分配将无效。
答案 1 :(得分:0)
类型参数U
是根据您将a
的结果分配到的addBox2(...)
变量的类型推断出来的,在您的情况下为A
。由于C
和D
都实现了A
,因此它可以完美运行。
以下当然不起作用:
S s = addBox2(new C(),new D());
A a = s; // compile error, type mismatch
答案 2 :(得分: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());
}
当我在getU()
中第一次拨打main()
时,我看到以下内容:
<? extends S> ? extends S analysis.Delme.getU(? extends S u1, ? extends S u2)
这显然不正确 - A
不会延伸S
。我怀疑Eclipse错误地过分简化了这里的泛型,并且更愿意报告C
和D
都来自S
,而不是同时实现A
。
如果我鼠标悬停在第二个电话上,我会更加合理:
<A> A analysis.Delme.getU(A u1, A u2)
这可能是编译器实际在这里为两个调用做的事情。
注意Thomas
指出,即使两个类没有任何共同点(例如S
和T
),它们仍然会从Object
延伸,所以{{1是有效的。
答案 3 :(得分:0)
不要相信编辑器的工具提示可能无法处理复杂的通用结构。推断类型为S & A & B
(在Java 8之前),可以将其分配给A
。在Java 8中,推断类型只是A
,因为泛型方法调用是使用目标类型的所谓多重表达式。对于addBox
调用,它没有任何区别。
为了说明问题,您可以写
Object o = Arrays.asList(new C(), new D());
在Eclipse中使用Java 7合规性模式,并将鼠标悬停在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
,而不是{ {1}}。
当您将语言合规性级别转换为Java 8时,编译器错误将消失,就像在Java 8中一样,目标类型将用于推断类型,它将是? extends S
而不是{{1 }}
如前所述,对于A
调用,无论是S & A & B
还是addBox
都是无误的,因为两者都可以分配到A
,因此可以在Java 7和Java 8合规级别。但是当你使用调用时,它会产生差异,就像使用S & A & B
一样,你可以看到实际推断的类型,它永远不会像编辑器的工具提示所声称的那样A
......