Aldsjers Dostrnik
Aldsjers Dostrnik

Reputation: 513

Java: Wildcards again

In my last question (thank you all that answer me), I have learnt the difference between List<Object> and List<?>.

However I still can't see the usefulness of wildcards.

I have two ArrayLists:

ArrayList<Integer> li = new ArrayList<Integer>(Arrays.asList(1,2,3));
ArrayList<String> ls = new ArrayList<String>(Arrays.asList("one","two","three"));

Now, look at the two blocks of code below:

static void printList(ArrayList<?> list) 
{
    for (Object elem: list)
        System.out.print(elem + " ");
    System.out.println();
}

and

static <T> void printList(ArrayList<T> list) 
{
    for (T elem: list)
        System.out.print(elem + " ");
    System.out.println();
}

When I call:

printList(li);
printList(ls);

Both methods return the output:

1 2 3
one two three

However the second solution, in the for loop, instead of Objects I use parametrized types (much more elegant I think).

So, the main question remains: Why do we need wildcards?

Upvotes: 16

Views: 781

Answers (8)

Bhaskar
Bhaskar

Reputation: 7523

Let's say you have this class hierarchy:

Fruit
Apple extends Fruit
Orange extends Fruit

...and some lists of each type of fruits:

List<Apple> apples ; and List<Orange> oranges ;

Now you want a List that can refer to any one of these lists.

So you declare List<? extends Fruit> fruits ;

You cannot use a type variable here.

Upvotes: 4

josefx
josefx

Reputation: 15656

One of the best uses for wildcards I found are complex nestings of Generic types.

A simple example with a list of Pair<T,E>, the wildcard makes the code shorter and more readable since everyone can see that I am only interested in the boolean value.

  ArrayList<Pair<Boolean, OneOrOtherLongClassName>> pairList = ...;
  for(Pair<Boolean,?> p:pairList)
  {
      if(p.first)count++;
  }

Upvotes: 1

Amit Deshpande
Amit Deshpande

Reputation: 19185

Because

you can modfify list in case of <T> list.add((T) new Object());

but you can not modify list in case of ? which ensures that list does not get modified. compiler generate error if you add anything other than null.

Collection<?> c = new ArrayList<String>();
c.add(new Object()); // Compile time error

Since we don't know what the element type of c stands for, we cannot add objects to it. The add() method takes arguments of type E, the element type of the collection. When the actual type parameter is ?, it stands for some unknown type. Any parameter we pass to add would have to be a subtype of this unknown type. Since we don't know what type that is, we cannot pass anything in. The sole exception is null, which is a member of every type.

On the other hand, given a List<?>, we can call get() and make use of the result. The result type is an unknown type, but we always know that it is an object. It is therefore safe to assign the result of get() to a variable of type Object or pass it as a parameter where the type Object is expected

More information on WildCards

Upvotes: 1

newacct
newacct

Reputation: 122429

You're right. Technically, every time you see a wildcard at the first level in the type of a parameter, you could create a new type variable for it, and use that type variable in the place of that wildcard, and it would work the same.

However, I would argue that is much less elegant than using a wildcard. A type variable is useful for express establishing constraints between different expressions, like when there are two parameters with the same type variable, or between a parameter and return type. A type variable used in just one place in the parameter is kind of a waste -- that was exactly what wildcards are meant for -- an "anonymous" type variable that is not needed anywhere else. When you just need to express "some type", without needing to constrain it with something else, why clutter your method signature with bonus type variables?

There are also other uses of wildcards that cannot be replaced type variables. For example, consider a method public List<?> foo(). It returns some kind of list, but you don't know a list of what (and it is unsafe to add anything to it). There is no way to express that using type variables without wildcards.

Also, consider wildcards in nested type parameters: you could have a variable of type List<List<?>>, i.e. a heterogenous list whose elements can be lists of different things. This also cannot be expressed without using wildcards.

Upvotes: 2

Roberto Mereghetti
Roberto Mereghetti

Reputation: 627

If the question is "usefulness of wildcards":

Wildcards are useful when only partial knowledge about the type parameter is required. "Partial knowledge" is implemented by the upper and lower bounds (? super T or ? extends T); if you use only the unbound wildcard ( ? ) you mean no knowledge at all, and you can't see where wildcards are really useful.

Wildcards can be used in composition with type parameters, to create relationships between the type of method parameters, return type and exception types. So an example of useful wildcard is

 class ListManager<T> {
    public void add(T item, List<? super T> list) {
        [... some useful operation ...]
         list.add(item);
    }
}

public class Main {
    public static void main(String[] args) {
        List<Object> list = new ArrayList<Object>();
        Integer item = 10;
        ListManager<Integer> manager = new ListManager<Integer>();
        manager.add(item, list);
    }
}

The method "ListManager.add()" creates a relationship between type of "list" and type of "item". The operation on "list" is always type safe, but you can use method "add" with list of different parameter type: we have used the minimum constraint on parameter "list".

(see also jls7 documentation)

Upvotes: 11

Priyank Doshi
Priyank Doshi

Reputation: 13151

According to Item 28-USE BOUNDED WILDCARDS TO INCREASE API FLEXIBILITY of effective java

if a type parameter appears only once in a method declaration, replace it with a wildcard.

Upvotes: 5

Jon Lin
Jon Lin

Reputation: 143846

In the Java Generics Tutorial it says:

Collection<?> c = new ArrayList<String>();
c.add(new Object()); // Compile time error

Since we don't know what the element type of c stands for, we cannot add objects to it. The add() method takes arguments of type E, the element type of the collection. When the actual type parameter is ?, it stands for some unknown type. Any parameter we pass to add would have to be a subtype of this unknown type. Since we don't know what type that is, we cannot pass anything in. The sole exception is null, which is a member of every type.

On the other hand, given a List, we can call get() and make use of the result. The result type is an unknown type, but we always know that it is an object. It is therefore safe to assign the result of get() to a variable of type Object or pass it as a parameter where the type Object is expected.

So having a wildcard type ensures that we can't add() to the List, with the exception of null. If you are just calling get() on the List, you know that it is at least an Object.

Upvotes: 7

Random42
Random42

Reputation: 9159

Using wildcards, your code is typesafe. In

List myList;

you can add any object you want. But in:

List<?> myList;

you can only add a null.

You should only use List, without type parameter, when you need the class (List.class) or when you need to check for type (instance of List).

Upvotes: 2

Related Questions