这实际上是合法的类型推断*。
我们可以将其简化为以下示例(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 finalInteger x = (Integer & IElement) element;
类型推断基本上适用于:
- 每个方法的类型参数的一组 推断变量 。
- 一组必须遵守的 界限 。
- 有时 约束 ,其被 降低 至界限。
在算法结束时,将根据绑定集将每个变量 解析 为交集类型,如果有效,则将编译调用。
该过程从8.1.3开始:
当推理开始时,通常从类型参数声明和关联的推理变量的列表中生成绑定集。这样的绑定集构造如下。对于每个 l(1≤l≤p) :
P1, ...,Pp``α1, ..., αp__
[…]
否则,对于在
TypeBound 中以T分隔的每种类型,界限将出现在集合[…]中。&
__α l <: T[P1:=α1, ..., Pp:=αp]
因此,这意味着首先编译器以
F <: Foo(
F的子类型为
Foo)的边界开始。
转到18.5.2,将考虑返回目标类型:
如果调用是多边形表达式,则[…]
R设为的返回类型m,T设为调用的目标类型,然后:
[…]
否则,约束公式将
‹R θ → T›被简化并与[绑定集]合并。
约束公式
‹R θ → T›被简化为的另一个边界
R θ <: T,因此我们有
F <: String。
稍后根据18.4解决:
[…] 为每个定义一个候选实例:
Ti``αi
- 否则,在适当的上限处,。
αi``U1, ..., Uk``Ti = glb(U1, ..., Uk)边界与当前边界集合并。
α1 = T1, ..., αn = Tn
回想一下我们的范围是
F <: Foo, F <: String。
glb(String, Foo)定义为
String &Foo。这显然是glb的合法类型,仅要求:
如果对于任何两个 类 ( 不是interfaces )和,都不是的子类,反之亦然,则是编译时错误。
Vi``Vj``Vi``Vj
最后:
如果解析通过实例化实例变量成功完成,则将其替换。然后:
T1, ..., Tp``α1, ..., αp``θ'``[P1:=T1, ...,Pp:=Tp]
- 如果不加以控制的转换是不必要的方法可应用于,然后的调用类型
m是通过将获得θ'到的类型m。
因此,
String & Foo以的类型调用该方法
F。我们当然可以将其分配给a
String,因此不可能将a转换
Foo为a
String。
String/
Integer是最后一堂课的事实显然未被考虑。
*注意:类型 清除 与问题完全无关。
另外,尽管这也可以在Java 7上编译,但是我认为可以不必担心那里的规范是合理的。Java 7的类型推断本质上是Java
8的较不复杂的版本。出于类似原因进行编译。
作为附录,虽然很奇怪,但这可能永远不会引起尚未出现的问题。编写其返回类型仅从返回目标推断出的泛型方法几乎是没有用的,因为只能
null从此类方法返回而无需强制转换。
例如,假设我们有一些映射类似物,它存储特定接口的子类型:
interface FooImplMap { void put(String key, Foo value); <F extends Foo> F get(String key);}class Bar implements Foo {}class Biz implements Foo {}进行如下所示的错误已经完全有效:
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;}可以,因为
Optional.empty()既不能产生也不能消耗
T。



