When to use wildcards in Java Generics?

22,058

Solution 1

The big difference between

public <T extends Animal> void takeThing(ArrayList<T> list)

and

public void takeThing(ArrayList<? extends Animal> list)

is that in the former method you can refer to "T" within the method as the concrete class that was given. In the second method you cannot do this.

Here a more complex example to illustrate this:

// here i can return the concrete type that was passed in
public <T extends Animal> Map<T, String> getNamesMap(ArrayList<T> list) {
    Map<T, String> names = new HashMap<T, String>();
    for (T animal : list) {
        names.put(animal, animal.getName()); // I assume there is a getName() method
    }
    return names;
}

// here i have to use general Animal
public Map<Animal, String> getNamesMap(ArrayList<? extends Animal> list) {
    Map<Animal, String> names = new HashMap<Animal, String>();
    for (Animal animal : list) {
        names.put(animal, animal.getName()); // I assume there is a getName() method
    }
    return names;
}

With the first method if you pass in an List of Cats you get a Map with Cat as key. The second method would always return a Map with general Animal key.

By the way this is not valid java syntax:

public <? extends Animal> void takeThing(ArrayList<?> list)

Using this form of generic method declaration you have to use a valid java identifier and not "?".

Edit:

The form "? extends Type" only applies to variable or parameter type declaration. Within a generic method declration it has to be "Identifier extends Type" as you are able to refer to the "Identifier" from within your method.

Solution 2

Wild cards are about co/contra variance of generics. I will try to make clear what this means by providing some examples.

Basically it is related to the fact that for types S and T, where S is a subtype of T, a generic type G<S> is not a valid subtype of G<T>

List<Number> someNumbers = new ArrayList<Long>(); // compile error

You can remedy this with wild cards

List<? extends Number> someNumbers = new ArrayList<Long>(); // this works

Please note, that you can not put anything into such a list

someNumbers.add(2L); //compile error

even (and more surprising for many developers):

List<? extends Long> someLongs = new ArrayList<Long>();
someLongs.add(2L); // compile error !!!

I think SO is not the right place to discuss that in detail. I will try to find some of the articles and papers that explain this in more detail.

Solution 3

Binding the type to a type parameter can be more powerful, depending on what the method is supposed to do. I'm not sure what takeThing is supposed to do, but imagine in general we have a method with one of these type signatures:

public <T extends Animal> void foo(ArrayList<T> list);

//or

public void foo(ArrayList<? extends Animal> list);

Here's a concrete example of something you can only do with the first type signature:

public <T extends Animal> void foo(ArrayList<T> list) {
    list.add(list.remove(0)); // (cycle front element to the back)
} 

In this case T is required to inform the type checker that the element being removed from the list is an OK element to add to the list.

You could not do this with a wildcard because, as the wildcard has not been bound to a type parameter, its context is not tracked (well, it is tracked, through "captures", but it's not available to leverage). You can get more information on this in another answer I've given: How do generics of generics work?

Solution 4

If you write ? extends T you say "anything that is a T or more specific". For example: a List<Shape> can have only Shapes in it, while a List<? extends Shape> can have Shapes, Circles, Rectangles, etc.

If you write ? super T you say "anything that is a T or more general". This is less often used, but has it's use cases. A typical example would be a callback: if you want to pass a Rectangle back to a callback, you can use Callback<? super Rectangle>, since a Callback<Shape> will be able to handle Rectangles as well.

Here's the relevant Wikipedia article.

Solution 5

If your takeThing method needs to add elements to the list parameter, the wildcard version will not compile.

The interesting case is when you are not adding to the list and both versions seem to compile and work.

In this case, you would write the wildcard version when you want to allow different type of animals in the list (more flexibility) and the parameter version when you require a fixed type of animal in the list: the T type.

For example the java.util.Collection declares:

interface Collection<E> {
  ...
  public boolean containsAll(Collection<?> c);
  ...
}

And suppose you have the following code:

Collection<Object> c = Arrays.<Object>asList(1, 2); 
Collection<Integer> i = Arrays.<Integer>asList(1, 2, 3); 
i.containsAll(c); //compiles and return true as expected

If the java.util.Collection would be:

interface Collection<E> {
    ...
    public boolean containsAll(Collection<E> c);
    ...
}

The above test code would not compile and the flexibility of the Collection API would be reduced.

It worth noting that the latter definition of containsAll has the advantage of catching more errors at compile time, for example:

Collection<String> c = Arrays.asList("1", "2"); 
Collection<Integer> i = Arrays.asList(1, 2, 3); 
i.containsAll(c); //does not compile, the integer collection can't contain strings

But misses the valid test with a Collection<Object> c = Arrays.<Object>asList(1, 2);

Share:
22,058
Koray Tugay
Author by

Koray Tugay

Crappy software developer who does not like self-promoting profiles.

Updated on December 05, 2020

Comments

  • Koray Tugay
    Koray Tugay over 3 years

    this is from HeadFirst Java: ( page 575 )

    This:

    public <T extends Animal> void takeThing(ArrayList<T> list)
    

    Does the same thing as this:

    public void takeThing(ArrayList<? extends Animal> list)
    

    So here is my question: if they are exactly same, why don't we write

    public <? extends Animal> void takeThing(ArrayList<?> list)
    

    or

    public void takeThing(ArrayList<T extends Animal> list)
    

    Also, when would it be useful to use a ? instead of a T in a method declaration ( as above ) with Generics, or for a Class declaration? What are the benefits?

  • orique
    orique almost 11 years
    Didn't know about super. Thanks
  • bennidi
    bennidi almost 11 years
    You don't need wild cards to say "anything that is T or subtype". You can simply say S extends T. Wild cards are necessary to make generics support co/contra variance.
  • bennidi
    bennidi almost 11 years
    By the way: Ceylon has a very nice way to deal with those specialties of type systems.
  • bennidi
    bennidi almost 11 years
    The big difference is not that you can not refer to ? as a type. If that were the only thing ? is good for, then it would be completely useless.
  • bennidi
    bennidi almost 11 years
    One rule of thumb is that you use "super" with incoming types (method parameters) and "extends" with outgoing (return types)
  • Werzi2001
    Werzi2001 almost 11 years
    @bennidi: What big differences do you see else in the case of a method declaration? You can give the same parameters in both cases. The generic method declaration gives you the benefit of refering to the type which enables higher type safty (which is definitly not useless).
  • bennidi
    bennidi almost 11 years
    Type safety is what you get when you use an identifier and refer to that within you method signature. I didn't say that this was useless. I said that "?" would be useless if its only capabilities were just to not be referable in the rest of the method. The big difference is that you can't add anything to List<? extends Animal>. Please, See me answer below.
  • Mark Peters
    Mark Peters almost 11 years
    I think you're on the right track, but you're creating a strawman by returning something that includes T. The example in the question is a void method.
  • Werzi2001
    Werzi2001 almost 11 years
    Yes that is correct. I only did this example to show that T can be used inside the method. It was only an example not directly related to the example in the question (as he asked for differences and usages).
  • newacct
    newacct almost 11 years
    "of something you can only do with the first type signature" Not true. I can have a method of the second signature call the method of the first signature. Then, to any outside code, they "do" the same task.
  • newacct
    newacct almost 11 years
    "If your takeThing method needs to add elements to the list parameter, the wildcard version will not compile." But where are you going to find a T to add to it anyway?
  • newacct
    newacct almost 11 years
    Yes but you are talking about a different example. With the original example, what is the need to refer to T inside the method?
  • newacct
    newacct almost 11 years
    the question is about parameters to a method
  • Mark Peters
    Mark Peters almost 11 years
    @newacct: Ok, that's a bit nitpicky. You still need the type parameter, just not externally visible. In the post I cite, I go into detail about how a helper generic method can help you "unleash" the wildcard capture. Doing that in this case just needlessly complicates things for no perceivable advantage.
  • Peter Becker
    Peter Becker almost 11 years
    @KorayTugay: I didn't really get your question due to the original formatting problem. I thought I give it a shot anyway.
  • Peter Becker
    Peter Becker almost 11 years
    @bennidi: I was using this in the context of creating lists, so we are talking about covariance.
  • newacct
    newacct almost 11 years
    Suppose you are writing an API. You would always use the second signature. Whether you internally use another method with the first signature is an implementation detail that should not be revealed to the outside. The point is, in this case, the type parameter serves no purpose in enforcing any constraints to the caller, since it is only used in one place in parameters. Its purpose happens to be completely internal. So it should be kept internal. Tomorrow you might change the method so that the parameter is no longer needed; the method signature shouldn't have to change.
  • Mark Peters
    Mark Peters almost 11 years
    @newacct: I don't disagree, but you're downvoting me as if the question was about the best approach for choosing an API signature, when the question was about what functionality one provides over the other. My main point is still exactly correct. Though you might introduce a level of indirection and hide it internally, you still admit that eventually you need a method with the first signature as I've mentioned. You can't do the job with only the second signature.
  • dcernahoschi
    dcernahoschi almost 11 years
    @newacct You might try to add via an Animal or Animal subclass reference.
  • Werzi2001
    Werzi2001 almost 11 years
    For the example in the question there are no need. But the question asked "Also, when would it be useful to use a ? instead of a T in a method declaration ( as above ) with Generics, or for a Class declaration? What are the benefits?" and i gave an example.
  • newacct
    newacct almost 11 years
    but then it won't be a T
  • Rap
    Rap almost 10 years
    This is the only question on the board that actually answers the question. The others only say that bounded types are superior to wildcards.
  • bennidi
    bennidi almost 10 years
    Thanks @Rap. Co and contra variance are not broadly understood and seem to continuously confuse people (including myself). I think Java's type system also doesn't offer the best solution. Ceylon or Scala for example offer much more mechanisms to handle generic polymorphism. BTW, upvote the answer :)
  • Koray Tugay
    Koray Tugay almost 10 years
    It does not even compile. @dcernahoschi
  • Koray Tugay
    Koray Tugay almost 10 years
    I think this is a great answer, but why would I ever want to use ?. Can you give an example for that as well?
  • Werzi2001
    Werzi2001 almost 10 years
    This is actually a good question. Personally i tend to always use the "Identifier extends Type" form as it has more possibilities and allows the same parameter types. Maybe someone else could give a hint why to prefer the ? syntax.
  • Cutberto Ocampo
    Cutberto Ocampo over 5 years
    Rahul, I think your response is perfect... for a different question. Perhaps if the question had been "When to use extends vs super when using wildcards", I've would voted for your answer. And no, I'm not being sarcastic, I actually liked your explanation because I'm trying to learn generics, and that part of wildcards was a little confusing until your answer came up.