Java泛型类型中的通配符参数在其范围内的正式条件是什么?

Wou*_*rts 11 java generics jls

使用Java中的参数化类型,检查参数是否在其绑定工作中的规则如何完全针对通配符?

鉴于这样的类:

class Foo<T extends Number> {}
Run Code Online (Sandbox Code Playgroud)

试验编译器接受的内容可以了解:

  • ? extends使用不相关的接口类型被允许通配符:Foo<? extends Runnable>是有效
  • 一个? extends使用一个不相关的类类型是不允许的通配符:Foo<? extends Thread>无效.这是有道理的,因为没有类型可以是两者的亚型NumberThread
  • ? super通配符中,通配符的下限必须是类型变量的边界的子类型:Foo<? super Runnable>不允许,因为Runnable它不是子类型Number.同样,这种限制非常有意义.

但这些规则在哪里定义?看看Java语言规范第4.5节,我没有看到任何区别于类的接口; 在应用我对JLS的解释时,Foo<? super Runnable>据说是有效的.所以我可能误解了一些东西.这是我的尝试:

从JLS的那一部分:

参数化类型由类或接口名称C和实际类型参数列表<T1,...,Tn>组成.如果C不是泛型类或接口的名称,或者实际类型参数列表中的类型参数的数量与C的已声明类型参数的数量不同,则是编译时错误.在下文中,每当我们说话时除了明确排除之外,我们还包括类或接口类型的通用版本.在本节中,让A1,...,An成为C的形式类型参数,并且让Bi为AI的声明边界.符号[Ai:= Ti]表示用类型Ti替换类型变量Ai,1 <= i <= n,并且在整个说明书中使用.

令P = G <T1,...,Tn>是参数化类型.必须是这样的情况,在P经历捕获转换(第5.1.10节)之后导致类型G <X1,...,Xn>,对于每个实际类型参数Xi,1 <= i <= n, Xi <:Bi [A1:= X1,...,An:= Xn](§4.10),或发生编译时错误.

将其应用于P = Foo<? super Runnable>:得到C = Foo,n = 1,T1 = ? super Runnable和B1 = Number.

对于捕获转换,捕获转换定义的这一部分适用:

如果Ti是表单的通配符类型参数?super Bi,则Si是一个新型变量,其上限为Ui [A1:= S1,...,An:= Sn],其下限为Bi.

这给出了G <X1,...,Xn> = Foo<X>其中X是一个带有上限Number和下限的新型变量Runnable.我没有看到任何明确禁止这种类型变量的东西.

B1 =中没有类型变量Number,因此Bi [A1:= X1,...,An:= Xn]仍然是简单的Number. X具有Number上限(来自捕获转换),并根据子类型规则 "类型变量的直接超类型是其绑定中列出的类型",所以X<:( Number= Bi [A1:= X1,... ,An:= Xn]),所以这个参数在它的范围内.(但事实并非如此!)

按照相同的推理,每个通配符都在其范围之内,所以这里有些东西是不对的......但是这个推理到底出错的地方呢?如何时,正确应用这些规则工作?

irr*_*ble 5

关于泛型的JLS是不完整的,你抓住了另一个漏洞.类型变量的下限几乎没有讨论过,我没有看到规范中有任何X上限Number和下限的限制Runnable.他们可能把它排除在外.

直观地说,必须至少有一种可能的类型满足类型变量的上限和下限,否则变量和使用该变量的所有类型都将是无用的.由于这几乎肯定是一个编程错误,编译应该失败.

很容易检查上限和下限是否为空类型.所有超类型的下界都是已知的; 至少其中一个应该是上限,否则没有类型在两个边界内.

-

这两个Foo<? extends A>案例在规范中有明确定义.通过捕获转换,我们有一个X带有上限的新类型变量A & Number,并且spec表示上限V1&...&Vm

如果对于任何两个类(不是接口)Vi和Vj,Vi不是Vj的子类,反之亦然,则是编译时错误.

因此,如果A = Thread,则捕获转换失败.