20

I am new to Java. In this document they give this as a use case for using wildcard:

static void printCollection(Collection c) {
    Iterator i = c.iterator();
    for (int k = 0; k < c.size(); k++) {
        System.out.println(i.next());
    }
}

This is their solution:

static void printCollection(Collection<?> c) {
    for (Object e : c) {
        System.out.println(e);
    }
}

But I could do the same without a wild card:

static <T> void printCollection(Collection<T> c) {
    Iterator i = c.iterator();
    for (int k = 0; k < c.size(); k++) {
        System.out.println(i.next());
    }
}

Can someone show me a simple use case where regular generics won't work but a wild card will?

Update: The answers over here When to use wildcards in Java Generics? do NOT tell us the need for wildcard. In fact its the other way around.

Community
  • 1
  • 1
developer747
  • 15,419
  • 26
  • 93
  • 147
  • I'm not entirely sure but I believe that the last version without wild card may spawn multiple versions of the same method specific to each `Collection` type upon calling the method with different collection types. The one with the wild card will handle them all. – tiguchi Apr 24 '15 at 20:32
  • 3
    @NobuGames In Java, it will be one method, because generics are erased at compile time. – ILMTitan Apr 24 '15 at 20:33
  • [the official docs](https://docs.oracle.com/javase/tutorial/java/generics/wildcardGuidelines.html) provide a good answer to this question – Droidman Apr 24 '15 at 21:06
  • Wildcards can (crappily) emulate covariance and contravariance. –  Apr 25 '15 at 05:17
  • 1
    What can Java do that assembly cannot do? – Juan Lopes Apr 25 '15 at 19:37
  • Maybe see this question for a pretty nice use case of wildcards with super and extends http://stackoverflow.com/questions/29869548/optional-and-return-type-narrowing – wastl Apr 25 '15 at 21:01

3 Answers3

4

One thing wildcards allow us to do is declare types that are agnostic towards a particular type parameter, for example a "list of any kind of list":

List<List<?>> listOfAnyList = ...;

listOfAnyList.add( new ArrayList<String>() );
listOfAnyList.add( new ArrayList<Double>() );

This is impossible without a wildcard:* because the element lists may have different types from each other.

And if we try to capture it, we will find that we can't:

static <E> void m(List<List<E>> listOfParticularList) {}

m( listOfAnyList ); // <- this won't compile

Another thing wildcards allow us to do that type parameters cannot is set a lower bound. (A type parameter can be declared with an extends bound, but not a super bound.**)

class Protector {
    private String secretMessage = "abc";

    void pass(Consumer<? super String> consumer) {
        consumer.accept( secretMessage );
    }
}

Suppose pass was instead declared to take a Consumer<String>. Now suppose we had a Consumer<Object>:

class CollectorOfAnything implements Consumer<Object> {
    private List<Object> myCollection = new ArrayList<>();

    @Override
    public void accept(Object anything) {
        myCollection.add( anything );
    }
}

The problem is: we can't pass it to a method accepting Consumer<String>. Declaring Consumer<? super String> means that we can pass any consumer which accepts a String. (Also see Java Generics: What is PECS?.)

Most of the time, wildcards just let us make tidy declarations.

If we don't need to use a type, we don't have to declare a type parameter for it.


* Technically also possible with a raw type, but raw types are discouraged.

** I don't know why Java doesn't allow super for a type parameter. 4.5.1. Type Arguments of Parameterized Types may hint that it has something to do with a limitation of type inference:

Unlike ordinary type variables declared in a method signature, no type inference is required when using a wildcard. Consequently, it is permissible to declare lower bounds on a wildcard […].

Community
  • 1
  • 1
Radiodef
  • 37,180
  • 14
  • 90
  • 125
  • Now if only you could also include a useful practical application of wildcards... because I've never found them – user541686 Apr 25 '15 at 07:32
  • @Mehrdad `Map, Something>` And how is `? super` not useful? It's used all over the place. – Radiodef Apr 25 '15 at 07:37
  • The way I see it, `Class>` is only necessary because raw types are discouraged, not because the wildcard adds any value. As for `? super`... I've never felt the need to use it, but maybe it's just me; where have you seen it be necessary? – user541686 Apr 25 '15 at 07:40
  • @Mehrdad Raw types and wildcards are just two sides of the same coin. I assume we can agree that *one* of them *is* necessary *occasionally*. `? super` is useful for writing code that's general e.g. [`Iterable#forEach(Consumer super T>)`](http://docs.oracle.com/javase/8/docs/api/java/lang/Iterable.html#forEach-java.util.function.Consumer-). – Radiodef Apr 25 '15 at 07:51
  • Yeah we do. Regarding the `? super` example, I guess that example makes sense in older versions of Java but it's also just as easy to make a method that accepts `T` and then calls the correct method. For newer versions, though, you just do `Iterable#forEach(Consumer)` and then call it as `forEach(x -> f(x))` and `f` plugs in correctly even if it accepts a superclass of `T`, right? – user541686 Apr 25 '15 at 08:00
  • @Mehrdad You're right. If we're using lambdas or method references, the rules are very permissive. http://ideone.com/0QhP4z – Radiodef Apr 25 '15 at 08:06
0

T stands for the generic type of that data structure. In your last example, you don't use it, and its NOT an actual type (for example String), and because you don't use it it doesn't really matter in this case.

For example, if you had a Collection and tried to pass it to a method that accepts a Collection, that works because there is no type T on the classpath so its considered a variable. If you tried passing the same Collection to a method that accepts a Collection, that would not work because you have String on your classpath so its not a variable.

Distjubo
  • 959
  • 6
  • 21
0

Take List as the example.

  • List<?> can be the parent class of List<A>.

for instance,

List<B> bList = new ArrayList<>(); // B is a class defined in advance
List<?> list = bList;

you can never use <T> in this situation.

  • <?> has the wildcard capture.

here,

  void foo(List<?> i) {
        i.set(0, i.get(0));
    }

the code above cannot be compiled. You can fix it:

    void foo(List<?> i) {
        fooHelper(i);
    }

    // wildcard can be captured through type inference.
    private <T> void fooHelper(List<T> l) {
        l.set(0, l.get(0));
    }

see more, http://docs.oracle.com/javase/tutorial/java/generics/capture.html

I can only think of the two currently, later may update.

chenzhongpu
  • 6,193
  • 8
  • 41
  • 79