wrongusername
wrongusername

Reputation: 18918

Generic vs wildcard unknown types

When is it recommended to do:

public <E> boolean hasPropertyX(List<E extends User> alist);

versus

public boolean hasPropertyX(List<? extends User> alist);

It would appear they both work just as well.

Upvotes: 8

Views: 726

Answers (5)

Radiodef
Radiodef

Reputation: 37845

Use ? extends when you only need to retrieve from the List:

User getElement(List<? extends User> list, int i) {
    return list.get(i);
}

Use ? super when you only need to add to the List:

void addElement(List<? super User> list, User u) {
    list.add(u);
}

Use E extends when you both need to retrieve and add:

<E extends User> void swapElements(List<E> list, int i, int j) {
    E temp = list.get(i);
    list.set(i, list.get(j));
    list.set(j, temp);
}
  • ? extends User: We don't know the exact type of the List, but we can retrieve a User from it.
  • ? super User: We don't know the exact type of the List, but we can put a User in it.
  • E extends User: We don't necessarily know the exact type of the List but it conforms to constraints such that:
    • We give its actual type the name E.
    • We know E is at least User.
    • We can both retrieve E from the List and put E in the List.

See also:

Upvotes: 1

Benjamin Winters
Benjamin Winters

Reputation: 64

Differences between generics and wildcard unknown types:

  • Enforcing a relationship on the types of method arguments (Use Generics)
  • Supporting multiple bounds (Use Generics)
  • Supporting both upper and lower bounds (Use Wildcard)

The related question:

When to use generic methods and when to use wild-card?

Upvotes: 2

Nayuki
Nayuki

Reputation: 18533

Explicitly naming the generic type as E and not ? has these uses (as far as I can think of):

0) To tie the return type to some part of the argument type - for example:

public <E> E getSomeElement(List<E> lst) { ... }
// ^ If we don't name the argument type as having E,
// then we can't specify the return type as being E

1) To tie some part of the argument type to some part of the enclosing type:

class Storage<E> {
    E item;
    public void replace(Storage<E> st) { item = st.item; }
    // ^ This wouldn't work if we wrote Storage<?> instead
}

2) To tie some combination of the argument types, return type, and enclosing type (see #0 and #1).

We can get away with the anonymous type name ? if we don't care about the actual type. Here is a basic example:

boolean allEqual(List<?> lst, Object y) {
    for (Object x : lst) {  // Any reference can be stored as Object
        if (!y.equals(x))  // equals takes an Object
            return false;
    }
    return true;
}
// ^ We could also rewrite this example with List<E> and "E x".

Another example:

int intSum(List<? extends Number> lst) {
    int sum = 0;
    for (Number x : lst)  // We only care that the list element is a Number
        sum += x.intValue();
    return sum;
}
// ^ We could also rewrite with List<E extends Number> and "E x".

Alternate reading: http://docs.oracle.com/javase/tutorial/extra/generics/wildcards.html

Upvotes: 2

kuujo
kuujo

Reputation: 8185

I suppose in that particular example they both do work effectively the same way in terms of type checking. However, if you extend the generic type to require a base or superclass of some class it can be useful. e.g.

public <E extends User> boolean hasPropertyX(List<E> alist);

This at least enforces that you're receiving some subclass of User.

EDIT

You can use a wildcard to achieve the same thing:

public boolean hasPropertyX(List<? extends User> alist);

But this won't work if, for example, you want to use the generic for multiple parameters:

public <E extends Automobile> void crashAutos(List<E> list1, List<E> list2);

This enforces the generic type on both arguments, whereas the following code does not force the two lists to contain the same type:

public void crashAutos(List<? extends Automobile> list1, List<? extends Automobile> list2);

I could call that method with two different subclasses of the Automobile class:

List<Car> cars = ...
List<Truck> trucks = ...
crashAutos(cars, trucks);

Whereas using generics enforces the same type for both arguments.

Upvotes: 2

Zielu
Zielu

Reputation: 8552

Without typed return value, the only difference I can think of is explicit typing of the first way of declaration during method call.

So for example you are using it inside typed class C<K extends String>

List<V extends String> input = ...;
boolean var = obj.hasProperty<K>(input);

will raise the compiler error. But why would any one want to do so...

Nice question even if most likely the answer is both are the same.

Upvotes: 4

Related Questions