8

As far as I know, using an upper bounded generic and using a superclass as a method parameter both accept the same possible arguments. Which is preferred, and what's the difference between the two, if any?

Upper bounded generic as parameter:

public <T extends Foo> void doSomething(T foo) {}

Superclass as parameter:

public void doSomething(Foo foo) {}
Someone
  • 551
  • 3
  • 14

2 Answers2

6

That's an upper bounded type parameter. Lower bounds are created using super, which you can't really do for a type parameter. You can't have a lower bounded type parameter.

And that would make a difference, if you, for example want to pass a List<T>. So, for the below two methods:

public <T extends Foo> void doSomething(List<T> foos) {}
public void doSomething(List<Foo> foo) {}

And for the given class:

class Bar extends Foo { }

The following method invocation:

List<Bar> list = new ArrayList<Bar>();
doSomething(list);

is valid for 1st method, but not for 2nd method. 2nd method fails because a List<Foo> is not a super type of List<Bar>, although Foo is super type of Bar. However, 1st method passes, because there the type parameter T will be inferred as Bar.

Community
  • 1
  • 1
Rohit Jain
  • 209,639
  • 45
  • 409
  • 525
  • I see. Is there any reason to use the superclass as the parameter though? Or is it just convention to use it when you can? – Someone Jan 27 '14 at 20:20
  • @Someone When you are just passing a single argument of type `Foo` or it's subtype, then you don't need a generic method. You can pass an instance of a subclass of `Foo` to a parameter type of `Foo`. – Rohit Jain Jan 27 '14 at 20:28
  • 1
    This answers a different question. In the original question, `Foo` is a supertype of `T`. Whereas in your case, `List` is not a supertype of `List`, so that's why it's different. In your case, a supertype for `List` would instead be `List extends Foo>`. – newacct Jan 28 '14 at 03:10
1

Generally, you only need a type variable when it's used in more than one place in class/method/field declarations. When you declare one on a method (rather than a class), the only places to use it are on the parameters and return value of that method.

For example, you can use it on multiple parameters to ensure their types match:

public static <T> void addToList(List<T> list, T element) {
    list.add(element);
}

This is a trivial example, but you can see that it prevents you from giving it an element that doesn't match the list's generic type:

List<Integer> list = new ArrayList<>();
addToList(list, 7);
//addToList(list, 0.7); // doesn't compile
//addToList(list, "a"); // doesn't compile

You can also declare a parameter and the return type to be the same type:

public static <T> T nullCheck(T value, T defValue) {
    return value != null ? value : defValue;
}

Since this method is returning one of the two T objects it's given, we can safely say that the returned object is also of type T.

Integer iN = null;
Integer i = nullCheck(iN, 7);
System.out.println(i); // "7"

Double dN = null;
Double d = nullCheck(dN, 0.7);
System.out.println(d); // "0.7"

Number n = nullCheck(i, d); // T = superclass of Integer and Double
System.out.println(n); // "7"

As for the example in the question, the type variable is only being used once, so it's equivalent to using the superclass. In this case you should avoid declaring a type variable, it's just unnecessary clutter.

Also I should note that the other answer changes the example to use List<T> and List<Foo>, but as mentioned in the comments, the superclass is really List<? extends Foo>, so no type variable is needed there, either.

Sean Van Gorder
  • 3,393
  • 26
  • 26