Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

What is the difference between bounded wildcard and type parameters?

Is there a difference between

<N extends Number> Collection<N> getThatCollection(Class<N> type) 

and

Collection<? extends Number> getThatCollection(Class<? extends Number>) 
like image 736
rrejex Avatar asked Nov 17 '09 17:11

rrejex


People also ask

What is a bounded wildcard?

A bounded wildcard is one with either an upper or a lower inheritance constraint. The bound of a wildcard can be either a class type, interface type, array type, or type variable. Upper bounds are expressed using the extends keyword and lower bounds using the super keyword.

What is a bounded type parameter?

There may be times when you want to restrict the types that can be used as type arguments in a parameterized type. For example, a method that operates on numbers might only want to accept instances of Number or its subclasses. This is what bounded type parameters are for.

What happens when we use a bounded wildcard?

Use an upper-bounded wild card only when values are to be retrieved and processed and the data structure (ArrayList) won't be changed. This means that the corresponding argument of a call on m2 can be an ArrayList whose elements are of any class that is Integer or a superclass of Integer, including Number and Object.


1 Answers

They expose different interfaces and contract for the method.

The first declaration should return a collection whose elements type is the same of the argument class. The compiler infers the type of N (if not specified). So the following two statements are valid when using the first declaration:

Collection<Integer> c1 = getThatCollection(Integer.class); Collection<Double> c2 = getThatCollection(Double.class); 

The second declaration doesn't declare the relationship between the returned Collection type argument to the argument class. The compiler assumes that they are unrelated, so the client would have to use the returned type as Collection<? extends Number>, regardless of what the argument is:

// Invalid statements Collection<Integer> c1 = getThatCollection(Integer.class);   // invalid Collection<Double> c2 = getThatCollection(Double.class);   // invalid Collection<Number> cN = getThatCollection(Number.class);   // invalid  // Valid statements Collection<? extends Number> c3 = getThatCollection(Integer.class);  // valid Collection<? extends Number> c4 = getThatCollection(Double.class);  // valid Collection<? extends Number> cNC = getThatCollection(Number.class);  // valid 

Recommendation

If indeed there is a relationship between the type between the returned type argument and the passed argument, it is much better to use the first declaration. The client code is cleaner as stated above.

If the relationship doesn't exist, then it is still better to avoid the second declaration. Having a returned type with a bounded wildcard forces the client to use wildcards everywhere, so the client code becomes clattered and unreadable. Joshua Bloch emphisize that you should Avoid Bounded Wildcards in Return Types (slide 23). While bounded wildcards in return types may be useful is some cases, the ugliness of the result code should, IMHO, override the benefit.

like image 103
notnoop Avatar answered Oct 11 '22 03:10

notnoop