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?
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.
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).
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With