使用 Java 中的参数化类型,检查参数是否在其范围内的规则如何完全适用于通配符?
给定这样的类:
class Foo<T extends Number> {}
尝试编译器接受的内容会了解到:
? extends
允许使用不相关接口类型的通配符:Foo<? extends Runnable>
有效? extends
不允许使用不相关类类型的通配符:Foo<? extends Thread>
无效。这是有道理的,因为没有类型可以是两者的子Number
类型Thread
- 在
? super
通配符中,通配符的下限必须是类型变量边界的子类型:Foo<? super Runnable>
不允许,因为Runnable
不是 的子类型Number
。同样,这个限制非常有意义。
但是这些规则是在哪里定义的?查看Java 语言规范第 4.5 节,我没有看到任何区分接口和类的东西。当应用我对 JLSFoo<? super Runnable>
的解释时,据说是有效的。所以我可能误解了一些东西。这是我的尝试:
从 JLS 的那个部分:
参数化类型由类或接口名称 C 和实际类型参数列表 <T1 , ... , Tn> 组成。如果 C 不是泛型类或接口的名称,或者实际类型实参列表中的类型实参数量与 C 声明的类型实参数量不同,则属于编译时错误。在下文中,无论何时我们说对于类或接口类型,我们也包括泛型版本,除非明确排除。在本节中,让 A1 , ... , An 是 C 的形式类型参数,让 Bi 是 Ai 的声明边界。符号 [Ai := Ti] 表示将类型变量 Ai 替换为类型 Ti,用于 1 <= i <= n,并在整个规范中使用。
令 P = G<T1, ..., Tn> 为参数化类型。必须是这样的情况,在对每个实际类型参数 Xi, 1 <= i <= n 进行捕获转换(第 5.1.10 节)导致类型 G<X1, ..., Xn> 之后, 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]),所以这个参数在它的范围内。(但事实并非如此!)
遵循相同的推理,每个通配符都在其范围内,所以这里有些地方是不对的……但是这个推理到底哪里出错了?这些规则在正确应用时如何运作?