Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Java Generics Wildcard vs Typed Generics usage

Tags:

java

generics

I am reading through the following section in the Java tutorial: http://docs.oracle.com/javase/tutorial/java/generics/capture.html

It starts off by saying that the following code produces an error due to the fact that a capture cannot be converted to an Object so the set method cannot confirm that Object is of type capture#1:

import java.util.List;

public class WildcardError {

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

I somewhat understand the reasoning behind this. i.get returns an Object, and the compiler cannot determine if the object is of type capture#1 so it cannot match it to the 2nd argument in a typesafe way.

It then recommends using the following code to make this method work:

public class WildcardFixed {

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


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

}

I somewhat understand why this code works as well, in this code, l.get is guaranteed to be of type T, so it can be passed as an argument of type T.

What I don't understand is why you couldn't just use a method like this, with no helper:

class GenericsTest {
    static <K> void bar(List<K> l) {
        l.set(0, l.get(l.size() - 1));
    }

    public static void main(String[] args) {
        List<Integer> lst = Arrays.asList(1, 2, 3, 4);
        bar(lst);
        System.out.println(lst); // [4, 3, 2, 4]
    }
}

i.e. if you're going to use type inference, why not just use explicit typed generics over a wildcard and a helper function? Is there any advantage to using wildcards in this scenario? In which cases would you actually prefer to use a wildcard over a typed generic?

like image 992
Shashank Avatar asked Nov 10 '22 02:11

Shashank


1 Answers

First note that void foo(List<?> i) and void <K> foo(List<K> i) both accept the exact same set of arguments -- assuming you don't explicitly specify a K on the generic method case, any argument that can be passed to one function signature can be passed to the other, and vice versa. So to "outside code", both signatures are equally useful.

Given that they are equivalent, the one that has fewer type parameters is simpler, and should always be preferred. When presenting a public API to outside code, you should always present it in the simplest form, only having the minimal type stuff necessary to make it safe. The type List<?> is sufficient to express that it can take any type of List, so that is what we should use.

We happen to use the type parameter K internally to solve some generics issues, but this is just an unfortunate internal implementation detail that outside code does not need to care about. Therefore, we should hide this ugliness and wrap it in a nicer function signature when making a public API.

In addition to abstraction purposes, another reason why you may want a particular signature is if it overrides a superclass method that has that signature. You cannot add type parameters when overriding.

like image 170
newacct Avatar answered Nov 15 '22 05:11

newacct