这实际上是一个合法的类型推断*。
我们可以这样降低到下面的例子(Ideone):
interface Foo {
<F extends Foo> F bar();
public static void main(String[] args) {
Foo foo = null;
String baz = foo.bar();
}
}
编译器被允许推断(无意义,真的)相交类型String & Foo
因为Foo
是一个接口。对于问题中的示例,推断出Integer & IElement
。
这是无意义的,因为转换是不可能的。我们不能做这样的投自己:
// won't compile because Integer is final
Integer x = (Integer & IElement) element;
类型推断基本符合工程:
- 一套推断变量每种方法的类型参数的。
- 一套界限必须符合。
- 有时限制,这是减少界限。
在算法结束时,每个变量是解决基于绑定集的交叉型,并且如果它们是有效的,则调用编译。
该过程开始于8.1.3:
当推理开始时,通常是从类型参数声明P1, ..., Pp
和相关联的推理变量α1, ..., αp
列表生成的绑定集合。这样的绑定集合构造如下。对于每个升(1 ≤ 升 ≤ P):
所以,这意味着第一编译器以一个结合的F <: Foo
(这意味着F
是Foo
亚型)开始。
移动到18.5.2,返回目标类型被考虑:
如果调用是聚表达,[&hellip;]让R
是m
返回类型,让T
是调用的目标类型,然后:
约束公式‹R θ → T›
被减少到另一个必然的R θ <: T
,所以我们有F <: String
。
在这些后来获得根据18.4解决:
[&hellip;]的候选实例Ti
为每个αi
定义:
- 否则,在
αi
有适当的上限U1, ..., Uk
,Ti = glb(U1, ..., Uk)
。
边界α1 = T1, ..., αn = Tn
与当前的绑定集合在一起。
回想一下,我们的边界集是F <: Foo, F <: String
。 glb(String, Foo)
定义为String & Foo
。这显然是对glb一个合法的类型,只要求:
这是一个编译时错误,如果,对于任何两个类(未接口)Vi
和Vj
,Vi
不是一个子类别Vj
或反之亦然。
最后:
如果分辨率与推理变量α1, ..., αp
实例T1, ..., Tp
成功,让θ'
是替代[P1:=T1, ..., Pp:=Tp]
。然后:
- 如果未经检查的转化率不必要的方法是适用的,则
m
调用类型是通过施加到θ'
的m
类型获得。
该方法因此具有String & Foo
援引为的F
类型。我们当然可以将其分配到String
,因此不可能将Foo
转换为String
。
显然不考虑String
/Integer
是最终类的事实。
*注:类型擦除是/是完全无关的问题。另外,虽然这也编译在Java 7上,但我认为可以合理地说我们不必担心那里的规范。 Java 7的类型推断实质上是Java 8的一个不太复杂的版本。它编译出于类似的原因。
作为附录,而奇怪的,这将有可能不会引起那是不存在的问题。编写一个返回类型只从返回目标中推断的泛型方法是很有用的,因为只有null
可以从这种方法返回而不需要转换。
假设的例子中,我们有一些地图模拟存储特定接口的亚型:
它已经完全有效的,使一个错误,如下列:
FooImplMap m = ...;
m.put("b", new Bar());
Biz b = m.get("b"); // casting Bar to Biz
这样的事实我们可以也做Integer i = m.get("b");
是不是新错误的可能性。如果我们是这样编程的代码,它已经可能不适合开始。
通常,只有在没有理由限制类型参数的情况下,才能从目标类型中推断出类型参数,例如, Collections.emptyList()
和Optional.empty()
:
private static final Optional<?> EMPTY = new Optional<>();
public static<T> Optional<T> empty() {
@SuppressWarnings("unchecked")
Optional<T> t = (Optional<T>) EMPTY;
return t;
}
这是A-OK因为Optional.empty()
既不能产生也不消耗T
。
这只是没有任何意义。 – Radiodef
Java版本是:1.7.0_45 32位 – nrainer
CharSequence可分配给字符串..有什么问题? –