使用Java中的参数化类型,如何检查参数是否在其绑定工作完全的通配符中的规则?
鉴于这样的课程:
class Foo<T extends Number> {}
尝试编译器接受的内容:
? extends
通配符:Foo<? extends Runnable>
有效? extends
通配符:Foo<? extends Thread>
无效。这是有道理的,因为任何类型都不能是Number
和Thread
? 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]仍然只是X
。
Number
有X
作为上限(来自捕获转换),并且根据the subtyping rules“类型变量的直接超类型是其绑定中列出的类型”,所以{{ 1}}&lt;:Number
(= Bi [A1:= X1,...,An:= Xn]),因此该参数在其范围内。 (但事实并非如此!)
遵循相同的推理每个通配符都在其范围内,所以这里的某些内容是不对的...但是这个推理究竟出错了? 这些规则在正确应用时如何工作?
答案 0 :(得分:5)
关于泛型的JLS是不完整的,你抓住了另一个漏洞。类型变量的下限几乎没有讨论,我没有看到X
具有上限Number
和下限Runnable
的规范中的任何限制。他们可能把它留了出来。
直观地说,必须至少有一种可能的类型满足类型变量的上限和下限,否则变量和使用该变量的所有类型都将是无用的。由于这几乎肯定是编程错误,编译应该失败。
很容易检查上限和下限是否为空类型。所有超类型的下界都是已知的;至少其中一个应该是上限,否则没有类型在两个边界内。
-
两个Foo<? extends A>
案例在规范中有明确定义。通过捕获转换,我们有新的类型变量X
,其上限为A & Number
,而规范则表示上限为V1&...&Vm
如果对于任何两个类(不是接口)Vi和Vj,Vi不是Vj的子类,反之亦然,那么这是编译时错误。
因此,如果A = Thread,则捕获转换失败。