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

时间:2011-08-09 21:07:20

标签: java generics jls

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

鉴于这样的课程:

class Foo<T extends Number> {}

尝试编译器接受的内容:

  • 允许使用不相关的接口类型的? extends通配符:Foo<? extends Runnable>有效
  • 不允许使用不相关类类型的? extends通配符:Foo<? extends Thread>无效。这是有道理的,因为任何类型都不能是NumberThread
  • 的子类型
  • ? super通配符中,通配符的下限必须是类型变量边界的子类型:Foo<? super Runnable>不允许Runnable,因为Number不是{Foo<? super Runnable>的子类型1}}。同样,这种限制也很有意义。

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

从JLS的那一部分开始:

  

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

     

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

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

对于捕获转换,definition of capture conversion的这一部分适用:

  

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

得到G&lt; X1,...,Xn&gt; = X其中Number是一个新的类型变量,其上限为Runnable,下限为Number。我没有看到任何明确禁止这种类型变量的内容。

B1 = Number中没有类型变量,因此Bi [A1:= X1,...,An:= Xn]仍然只是XNumberX作为上限(来自捕获转换),并且根据the subtyping rules“类型变量的直接超类型是其绑定中列出的类型”,所以{{ 1}}&lt;:Number(= Bi [A1:= X1,...,An:= Xn]),因此该参数在其范围内。 (但事实并非如此!)

遵循相同的推理每个通配符都在其范围内,所以这里的某些内容是不对的...但是这个推理究竟出错了? 这些规则在正确应用时如何工作?

1 个答案:

答案 0 :(得分:5)

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

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

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

-

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

  

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

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