简体   繁体   中英

What is the benefit of an upper bounded wildcard?

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.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM