The recurring explanation I find is that an upper bounded wildcard relaxes the restrictions of types that a type parameter can accept. This concept applies to bounded generics as well, for example:
static <T extends Number> void gMethod (ArrayList <T> list) {}
This method's generic will accept Objects of type Number or any of it's sub classes when specified:
ArrayList <Integer> intList = new ArrayList();
gMethod(intList); // allowed
For further elaboration that a generic bounded to Number will accept type arguments of Number or any of it's sub classes as well:
class Thing <T extends Number> {}
Thing <Number> numThing = new Thing();
Thing <Integer> intThing = new Thing();
Thing <Double> dubThing = new Thing(); // All three instances work
Given this, the only benefit I can see to using an upper bounded wildcard vs a bounded generic is that an upper bounded wildcard type argument can be declared without relying on a type parameter already declared by either a class or method. Is there a more important benefit that I'm missing?
„ …the only benefit I can see to using an upper bounded wildcard vs a bounded generic… “
If your use case only ever calls for you working with one Thing
at a time, then the simple usage scenario you outline is all you'll ever need.
But eventually you'll have a use case where you will need to work with a heterogeneous assortment of Things
. That's when you'll need to pull slightly more advanced polymorphism out from your toolbox.
„ …Is there a more important benefit that I'm missing?… “
One super important benefit that it sounds like you're missing is a substitutability relationship between types; called covariance .
For example, since it's legal to do this:
Integer[] intAry = {2,4,6,8};
Number[] numAry = intAry;
Then intuitively, it seems like you should be able to do this:
List<Integer> intList = List.of(8,6,7,5,3,0,9);
List<Number> numList = intList; // but this fails to compile
A wildcard with an upper bound effectively makes collections covariant:
List <? extends Number> numList = intList;
Since Integer
extends Number
:
Thing<Number> numThing = new Thing<>();
Thing<Integer> intThing = new Thing<>();
Then intuitively, it seems like you should be able to do this:
numThing = intThing; // but this fails to compile
A wildcard with an upper bound effectively makes Things
more intuitive:
Thing<? extends Number> numThing = new Thing<>();
numThing = intThing; /* That makes sense! */
Same deal with methods. With this declaration:
public static void use(Thing<Number> oneThing){
/*...*/
}
This would fail to compile:
Thing<Integer> intThing = new Thing<>();
use(intThing); /* error: no suitable method found for use(Thing<Integer>) */
Wild cards with upper bounds makes it possible to use Things
the way you intuitively would think they'd be used:
public static void use(Thing<? extends Number> anyThing){
/* ...*/
}
...
Thing<Integer> intThing = new Thing<>();
use(intThing); /* Perfectly fine! */
„ …applies to bounded generics …This method's generic will accept…an upper bounded wildcard vs a bounded generic … “
The things you've incorrectly called „ generics “ are actually called either type parameters , type variables or type arguments ; depending on the context .
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.