|
Re: Why no <T super Fruit> ?
|
Posted: Nov 10, 2005 6:58 AM
|
|
Lower bounds <T super Fruit> would in fact be very useful as method bounds. So not including them in Java 1.5 was an oversight, I think. For instance, consider a generic class List<A> of immutable lists with an append method. Here's how one would write it without lower bounds:
class List<A> {
List<A> append(List<A> that) { ... }
}
The problem with that method is that the type parameter A appears contravariantly. Hence, you cannot apply append to lists whose type is not known precisely. E.g.
List<Fruit> myFruits;
List<? extends Fruit> yourFruits;
yourFruits.append(myFruits) // ERROR!!
This behaviour seems strange because our lists are immutable; Nevertheless there are still operations which are `forgotten' when the list type parameter is a wildcard.
How can we remedy the situation? With a lower bound, one could write the list class as follows:
class List<A> {
<B super A> List<B> append(List<B> that) { ... }
}
In other words, we have generalized append , so that it accepts as parameters lists of an arbitrary supertype of the list element type A and returns lists of the same type. Technically speaking, the type parameter A now appears in covariant position, and therefore append is not forgotten as a member of lists with wildcard type arguments.
yourFruits.append(myFruits)
would work perfectly, and would give a List<Fruit> .
apples.append(oranges)
would work also, and would again give a list of fruits.
Lower bounds are used extensibely in Scala's libraries. Scala has declaration site-variance instead of wildcards. I.e. you can define that your lists are covariant by prefixing the type parameter with a `+' sign:
// Scala code
class List[+A] {
def append[B >: A](that: List[B]): List[B] = ...
}
In our experience the combination of declaration-site variance and lower bounds is a great guide in making variances in libraries correct and consistent. In Scala, one would first decide that lists are covariant. More likely than not one would then give the first, erroneous definition of append:
// Scala code
def append(that: List[A]): List[A] = ...
When compiling the List class, the scala compiler would then complain that A is declared to be covariant, but appears contra-variantly in append . Prompted by this error, the programmer would then put in the lower bound (he needs to be educated about this, of course, but essentially the same trick works in many variance problems).
|
|