Java 泛型类型中的通配符参数在其范围内的形式条件是什么?
对于 Java 中的参数化类型,检查参数是否在其绑定范围内的规则如何完全适用于通配符?
给定一个这样的类:
class Foo<T extends Number> {}
试验编译器接受的内容得知:
- <代码>?允许使用不相关的接口类型的 extends 通配符:
Foo
有效 - A
?不允许使用不相关的类类型来扩展
通配符:Foo
无效。 没有任何类型可以同时是Number
和Thread
的子类型 - 这是有道理的,因为在
中 ? super
通配符,通配符中的下界必须是类型变量的下界的子类型:Foo
,因为Runnable
不是Number
的子类型。再说一次,这个限制是完全有道理的。
但这些规则是在哪里定义的呢?查看 Java 语言规范第 4.5 节,我没有看到任何区分接口和类的东西;当应用我对 JLS Foo
据说是有效的。所以我可能误解了一些东西。这是我的尝试:
来自 JLS 的该部分:
参数化类型由类或接口名称 C 和实际类型参数列表
组成。如果 C 不是泛型类或接口的名称,或者实际类型参数列表中类型参数的数量与 C 声明的类型参数的数量不同,则会出现编译时错误。对于类或接口类型,我们也包括通用版本,除非明确排除。在本节中,令 A1 , ... , An 为 C 的形式类型参数,并令 Bi 为 Ai 的声明边界。符号 [Ai := Ti] 表示用类型 Ti 替换类型变量 Ai,即 1 <= i <= n,并在本规范中使用。 令P=G<T1,...,Tn>是参数化类型。情况一定是,在 P 进行捕获转换(第 5.1.10 节)后,产生类型 G
,对于每个实际类型参数 Xi,1 <= i <= n , Xi <: Bi[A1 := X1, ..., An := Xn] (§4.10),或者发生编译时错误。
将其应用于 P = Foo
:给出 C = Foo
,n = 1,T1 = ?超级可运行
和B1 = 数字
。
对于捕获转换,捕获转换定义的这一部分 适用:
如果 Ti 是 ? 形式的通配符类型参数super Bi,则 Si 是一个新鲜类型变量,其上界为 Ui[A1 := S1, ..., An := Sn],下界为 Bi。
这给出GFoo
其中 X
是一个新的类型变量,其上限为 Number
,下限为 Runnable
。我没有看到任何明确禁止此类类型变量的内容。
B1 = Number
中没有类型变量,因此 Bi[A1 := X1, ..., An := Xn] 仍然只是 Number
。 X
将 Number
作为上限(来自捕获转换),并根据 子类型规则 “类型变量的直接超类型是其绑定中列出的类型”,因此 X
<: Number
(= Bi[A1 := X1, ..., An := Xn]),因此该参数在其范围内。 (但事实并非如此!)
按照同样的推理,每个通配符都在其范围内,所以这里有些东西是不对的......但是这个推理到底错在哪里?如果正确应用,这些规则如何发挥作用?
With parameterized types in Java, how do the rules that check if a parameter is within its bound work exactly for wildcards?
Given a class like this:
class Foo<T extends Number> {}
Experimenting with what the compiler accepts learns that:
- A
? extends
wildcard using an unrelated interface type is allowed:Foo<? extends Runnable>
is valid - A
? extends
wildcard using an unrelated class type is not allowed:Foo<? extends Thread>
is invalid. That makes sense because no type can be a subtype of bothNumber
andThread
- In a
? super
wildcard, the lower bound in the wildcard must be subtype of the bound of the type variable:Foo<? super Runnable>
is not allowed becauseRunnable
is not a subtype ofNumber
. Again, this restriction makes perfect sense.
But where are these rules defined? Looking at the Java Language Specification section 4.5, I don't see anything distinguishing interfaces from classes; and when applying my interpretation of the JLS Foo<? super Runnable>
is said to be valid. So I probably misunderstood something. Here's my attempt:
From that section of the JLS:
A parameterized type consists of a class or interface name C and an actual type argument list <T1 , ... , Tn>. It is a compile time error if C is not the name of a generic class or interface, or if the number of type arguments in the actual type argument list differs from the number of declared type parameters of C. In the following, whenever we speak of a class or interface type, we include the generic version as well, unless explicitly excluded. Throughout this section, let A1 , ... , An be the formal type parameters of C, and let be Bi be the declared bound of Ai. The notation [Ai := Ti] denotes substitution of the type variable Ai with the type Ti, for 1 <= i <= n, and is used throughout this specification.
Let P = G<T1, ..., Tn> be a parameterized type. It must be the case that, after P is subjected to capture conversion (§5.1.10) resulting in the type G<X1, ..., Xn>, for each actual type argument Xi, 1 <= i <= n , Xi <: Bi[A1 := X1, ..., An := Xn] (§4.10), or a compile time error occurs.
Apply that to P = Foo<? super Runnable>
: that gives C = Foo
, n = 1, T1 = ? super Runnable
and B1 = Number
.
For capture conversion this part of the definition of capture conversion applies:
If Ti is a wildcard type argument of the form ? super Bi, then Si is a fresh type variable whose upper bound is Ui[A1 := S1, ..., An := Sn] and whose lower bound is Bi.
That gives G<X1, ..., Xn> = Foo<X>
where X
is a fresh type variable with upper bound Number
and lower bound Runnable
. I don't see anything explicitly forbidding such a type variable.
There are no type variables in B1 = Number
, so Bi[A1 := X1, ..., An := Xn] is still simply Number
.X
has Number
as upper bound (coming from the capture conversion), and according to the subtyping rules "The direct supertypes of a type variable are the types listed in its bound", so X
<: Number
(= Bi[A1 := X1, ..., An := Xn]), so this parameter is within its bounds. (But it isn't!)
Following the same reasoning every wildcard is within its bounds, so something here isn't right... But where exactly did this reasoning go wrong? How do these rules work when applied correctly?
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(1)
关于泛型的 JLS 并不完整,您发现了其中的另一个漏洞。类型变量的下限几乎没有被讨论,并且我在具有上限
Number
和下限Runnable
的X
规范中也没有看到任何限制>。他们可能把它遗漏了。直观上,必须至少有一种可能的类型同时满足类型变量的上限和下限,否则该变量以及使用该变量的所有类型都将毫无用处。由于这几乎肯定是一个编程错误,因此编译应该失败。
很容易检查上限和下限是否构成空类型集。下界的所有超类型都是已知的;其中至少一个应该是上限,否则没有任何类型同时位于两个边界内。
--
两个
Foo
的情况在规范中得到了很好的定义。通过捕获转换,我们有了新的类型变量X
,其上限为A & Number
,规范表示上限V1&...&Vm
因此,如果A=Thread,则捕获转换失败。
JLS on generics is incomplete, and you caught another hole in it. Lower bound on type variables is barely discussed, and I don't see any restriction in spec either on
X
having upper boundNumber
and lower boundRunnable
. They probably left it out.Intuitively, there must be at least one possible type that satisfies both upper bound and lower bound of a type variable, otherwise the variable and all types using the variable would be useless. Since this is almost certainly a programming mistake, compile should fail.
It's easy to check whether upper bound and lower bound make an empty set of types. All super types of the lower bound are known; at least one of them should be the upper bound, otherwise there is no type that's within both bounds.
--
The two
Foo<? extends A>
cases are well defined in the spec. With capture conversion, we have new type variableX
with upper boundA & Number
, and the spec says for an upper boundV1&...&Vm
Therefore if A=Thread, capture conversion fails.