Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Java generics - purpose of wildcard except for lower bounds?

Tags:

java

generics

What is the purpose of using a wildcard for unbounded or upper-bounded generics? More specifically:

Why would I say public static void foo(List<?> bar) instead of public static <E> void foo(List<E> bar)?

Why would I say public static void foo(List<? extends Baz> bar) instead of public static <E extends Baz> void foo(List<E> bar)?

like image 526
Kvass Avatar asked Jan 08 '15 01:01

Kvass


3 Answers

If you are not ever going to refer to E in the code that follows, there is no need to declare it.

Reduces programmer memory strain.

like image 81
Thilo Avatar answered Oct 10 '22 11:10

Thilo


The versions with a wildcard are preferred. If a parameter has type List<?> it is clear that any List will be accepted. If any List is accepted, there's no reason to give the type parameter a name, so writing <E> would just be clutter. On the other hand, if the type parameter appears twice in the signature, then you cannot use wildcards. For example, this signature needs a type parameter.

public static <E> List<E> combineLists(List<E> list1, List<E> list2)

Actually in that example it would probably be better if the arguments had type List<? extends E> (the only way to do that without wildcards would be to have three type parameters, a total mess).

In Effective Java, it is recommended that even if the type parameter is needed in the body of the method, you should prefer the version of the signature with a wildcard, and write a private helper method to make this possible. For example:

public static void swapFirstAndLast(List<?> list) {
    helper(list);
}

private static <E> void helper(List<E> list) {
    int size = list.size();
    E e = list.get(0);
    list.set(0, list.get(size - 1)); // These lines would not be possible
    list.set(size - 1, e);           // If the type of list were List<?>
}
like image 29
Paul Boddington Avatar answered Oct 10 '22 13:10

Paul Boddington


If the type of the list is decided at runtime, then you will need to use wildcards.

The following program will print a List<String> if run with any command-line arguments; otherwise it will print a List<Number>:

public class Test {

    public static List<String> listOfStrings = Arrays.asList("hello", "world");
    public static List<Number> listOfNumbers = Arrays.asList(1, 2, 3, 4.5);

    public static List<?> getListOfUnknown(boolean arg) {
        if(arg) return listOfStrings;
        else return listOfNumbers;
    }

    public static void main(String[] args) {
        System.out.println(getListOfUnknown(args.length > 0));
    }
}
like image 32
user253751 Avatar answered Oct 10 '22 11:10

user253751