What are the formal conditions for a wildcard parameter in a Java generic type to be within its bounds?
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 Numb开发者_运维知识库er
.
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?
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 bound Number
and lower bound Runnable
. 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 variable X
with upper bound A & Number
, and the spec says for an upper bound V1&...&Vm
It is a compile-time error if for any two classes (not interfaces) Vi and Vj,Vi is not a subclass of Vj or vice versa.
Therefore if A=Thread, capture conversion fails.
精彩评论