Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How would a "supplementary private method" help avoid raw types?

Tags:

java

In the source code of java.util.Collection there is function called shuffle:

@SuppressWarnings({"rawtypes", "unchecked"})
public static void shuffle(List<?> list, Random rnd) {
    int size = list.size();
    if (size < SHUFFLE_THRESHOLD || list instanceof RandomAccess) {
        for (int i=size; i>1; i--)
            swap(list, i-1, rnd.nextInt(i));
    } else {
        Object arr[] = list.toArray();

        // Shuffle array
        for (int i=size; i>1; i--)
            swap(arr, i-1, rnd.nextInt(i));

        // Dump array back into list
        // instead of using a raw type here, it's possible to capture
        // the wildcard but it will require a call to a supplementary
        // private method
        ListIterator it = list.listIterator();
        for (int i=0; i<arr.length; i++) {
            it.next();
            it.set(arr[i]);
        }
    }
}

The comment in the code says, "instead of using a raw type here, it's possible to capture the wildcard but it will require a call to a supplementary private method."

What does that mean? How could this be written without raw types?

like image 911
Oliver Avatar asked Sep 03 '20 02:09

Oliver


2 Answers

There is a page in the Oracle Docs that explains what "captures a wildcard" means.

In the case of shuffle, you extract the "dump array" operation into a generic helper method:

private static <T> void dumpArray(Object[] arr, List<T> list) {
    ListIterator<T> it = list.listIterator();
    for (int i=0; i<arr.length; i++) {
        it.next();
        it.set((T)arr[i]);
    }
}

//...
dumpArray(arr, list);

This works because as the docs said:

Thanks to the helper method, the compiler uses inference to determine that T is CAP#1, the capture variable, in the invocation.

like image 189
Sweeper Avatar answered Oct 13 '22 12:10

Sweeper


While Sweeper perfectly answered the question, I am adding a bit more context on the raw context warning, and what could have been the developer's intention there (disclaimer, I am not the one wrote that code, I am just speculating)

According to the doc,

A raw type is the name of a generic class or interface without any type arguments

So in the above code, ListIterator it is a raw type. And you are assigning ListIterator of list which is a wildcard type List<?>.

Now what might be the issue in the above code using raw types? When you use a raw type to directly invoke a generic method defined in the corresponding generic types, the compiler will throw a warning. It will show warning for unchecked invocation as you are bypassing the generic type check, and that can cause a runtime error for type-mismatch. In this case, the following is doing that:

it.set((T)arr[i]);

Here arr[i] is the wildcard type created from List<?> but it is a raw ListIterator. So by design, compiler will show a warning. To get rid of that warning the developer suppressed that using:

@SuppressWarnings({"rawtypes", "unchecked"})

Now, a better workaround would be to have a private generic method to create a generic ListIterator and dump the array there. But the developer didn't seem to like that idea (although I think that would be cleaner).

like image 36
stinepike Avatar answered Oct 13 '22 12:10

stinepike