Java: bounded wildcards or bounded type parameter?
Asked Answered
C

5

98

Recently, I read this article: http://download.oracle.com/javase/tutorial/extra/generics/wildcards.html

My question is, instead of creating a method like this:

public void drawAll(List<? extends Shape> shapes){
    for (Shape s: shapes) {
        s.draw(this);
    }
}

I can create a method like this, and it works fine:

public <T extends Shape> void drawAll(List<T> shapes){
    for (Shape s: shapes) {
        s.draw(this);
    }
}

Which way should I use? Is wildcard useful in this case?

Canvasback answered 15/8, 2010 at 8:19 Comment(4)
? is a shorthand notation; internally compiler replaces it with a type parameter anyway; when there's compiler error, you'll see the surrogate type parameter, instead of the ?Joachim
correction: my previous comment is WRONG. wildcard is more sophisticated than I thought.Joachim
@Joachim while it is more complicated indeed, your point about replacing it with a type parameter is entirely valid; it's just one that you can't declare.Ephesians
if we look at just these two methods as they are - there is no difference and just a matter of style; otherwise (if you add some more generic parameters), things will changeEphesians
F
150

It depends on what you need to do. You need to use the bounded type parameter if you wanted to do something like this:

public <T extends Shape> void addIfPretty(List<T> shapes, T shape) {
    if (shape.isPretty()) {
       shapes.add(shape);
    }
}

Here we have a List<T> shapes and a T shape, therefore we can safely shapes.add(shape). If it was declared List<? extends Shape>, you can NOT safely add to it (because you may have a List<Square> and a Circle).

So by giving a name to a bounded type parameter, we have the option to use it elsewhere in our generic method. This information is not always required, of course, so if you don't need to know that much about the type (e.g. your drawAll), then just wildcard is sufficient.

Even if you're not referring to the bounded type parameter again, a bounded type parameter is still required if you have multiple bounds. Here's a quote from Angelika Langer's Java Generics FAQs

What is the difference between a wildcard bound and a type parameter bound?

A wildcard can have only one bound, while a type parameter can have several bounds. A wildcard can have a lower or an upper bound, while there is no such thing as a lower bound for a type parameter.

Wildcard bounds and type parameter bounds are often confused, because they are both called bounds and have in part similar syntax. […]

Syntax:

  type parameter bound     T extends Class & Interface1 & … & InterfaceN

  wildcard bound  
      upper bound          ? extends SuperType
      lower bound          ? super   SubType

A wildcard can have only one bound, either a lower or an upper bound. A list of wildcard bounds is not permitted.

A type parameter, in constrast, can have several bounds, but there is no such thing as a lower bound for a type parameter.

Quotes from Effective Java 2nd Edition, Item 28: Use bounded wildcards to increase API flexibility:

For maximum flexibility, use wildcard types on input parameters that represent producers or consumers. […] PECS stands for producer-extends, consumer-super […]

Do not use wildcard types as return types. Rather than providing additional flexibility for your users, it would force them to use wildcard types in client code. Properly used, wildcard types are nearly invisible to users of a class. They cause methods to accept the parameters they should accept and reject those they should reject. If the user of the class has to think about wildcard types, there is probably something wrong with the class's API.

Applying the PECS principle, we can now go back to our addIfPretty example and make it more flexible by writing the following:

public <T extends Shape> void addIfPretty(List<? super T> list, T shape) { … }

Now we can addIfPretty, say, a Circle, to a List<Object>. This is obviously typesafe, and yet our original declaration was not flexible enough to allow it.

Related questions


Summary

  • Do use bounded type parameters/wildcards, they increase flexibility of your API
  • If the type requires several parameters, you have no choice but to use bounded type parameter
  • if the type requires a lowerbound, you have no choice but to use bounded wildcard
  • "Producers" have upperbounds, "consumers" have lowerbounds
  • Do not use wildcard in return types
Farron answered 15/8, 2010 at 8:29 Comment(6)
Thank you very much, your explanation is very clear and instructiveCanvasback
@Tony: The book also has a short discussion on how to choose between wildcard vs type parameter when there is no bound. Essentially, if the type parameter appears only once in a method declaration, use wildcard. See also the reverse(List<?>) example from JLS java.sun.com/docs/books/jls/third_edition/html/…Farron
I am getting the UnsupportedOperationException for the following code, <code>public static <T extends Number> void add(List<? super T> list, T num) { list.add(num); }</code>Tersina
Also - you cant pass ? as a method parameter like doWork(? type) because then you can not use that parameter in method. You need to use type parameter.Chlorosis
@Farron How can I write replacement of public <T extends Shape> void addIfPretty(List<T> shapes, T shape) using wildcards, which does not give compiler error, like you mentioned which is NOT TYPE SAFE? I am still not able to understand any use case which cannot be done using type parameter, but can be done using wildcardSoosoochow
@VivekVardhan you can't create such a method using wildcards, that is the point. When deciding which to use, this is one of the best sources explaining.Ephesians
C
7

In your example you don't really need to use T, since you don't use that type anywhere else.

But if you did something like:

public <T extends Shape> T drawFirstAndReturnIt(List<T> shapes){
    T s = shapes.get(0);
    s.draw(this);
    return s;
}

or like polygenlubricants said, if you want to match the type parameter in the list with another type parameter:

public <T extends Shape> void mergeThenDraw(List<T> shapes1, List<T> shapes2) {
    List<T> mergedList = new ArrayList<T>();
    mergedList.addAll(shapes1);
    mergedList.addAll(shapes2);
    for (Shape s: mergedList) {
        s.draw(this);
    }
}

In the first example you get a bit more type safety then returning just Shape, since you can then pass the result to a function that may take a child of Shape. For example you may pass a List<Square> to my method, and then pass the resulting Square to a method that only takes Squares. If you used '?' you would have to cast the resulting Shape to Square which would not be type safe.

In the second example you ensure that both lists have the same type parameter (which you can't do with '?', since each '?' is different), so that you can create a list that contains all elements from both of them.

Certification answered 15/8, 2010 at 8:50 Comment(1)
I believe Shape s = ... should be T s = ..., otherwise return s; shouldn't compile.Farron
C
2

Consider following example from The Java Programming by James Gosling 4th edition below where we want to merge 2 SinglyLinkQueue:

public static <T1, T2 extends T1> void merge(SinglyLinkQueue<T1> d, SinglyLinkQueue<T2> s){
    // merge s element into d
}

public static <T> void merge(SinglyLinkQueue<T> d, SinglyLinkQueue<? extends T> s){
        // merge s element into d
}

Both of the above methods have the same functionality. So which is preferable? Answer is 2nd one. In the author's own words :

"The general rule is to use wildcards when you can because code with wildcards is generally more readable than code with multiple type parameters. When deciding if you need a type variable, ask yourself if that type variable is used to relate two or more parameters, or to relate a parameter type with the return type. If the answer is no, then a wildcard should suffice."

Note: In book only second method is given and type parameter name is S instead of 'T'. First method is not there in the book.

Claudy answered 19/7, 2015 at 0:26 Comment(0)
G
2

As far as I understand, the wildcard allows for more concise code in situations where a type parameter is not required (e.g. because it's referenced at several places or because multiple bounds are required as detailed in other answers).

In the link you indicate I read (under "Generic Methods") the following statements which hint in this direction:

Generic methods allow type parameters to be used to express dependencies among the types of one or more arguments to a method and/or its return type. If there isn't such a dependency, a generic method should not be used.

[...]

Using wildcards is clearer and more concise than declaring explicit type parameters, and should therefore be preferred whenever possible.

[...]

Wildcards also have the advantage that they can be used outside of method signatures, as the types of fields, local variables and arrays.

Gnat answered 15/3, 2018 at 13:41 Comment(0)
S
0

The second way is a bit more verbose, but it allows you to refer T inside it:

for (T shape : shapes) {
    ...
}

That's the only difference, as far as I understand.

Scalise answered 15/8, 2010 at 8:28 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.