英文:
How would a "supplementary private method" help avoid raw types?
问题
在 java.util.Collection
的源代码中有一个名为 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]);
}
}
}
代码中的注释说:“在这里不使用原始类型,可以捕获通配符,但这将需要调用一个附加的私有方法。”
这是什么意思?如何在不使用原始类型的情况下编写这段代码?
英文:
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?
答案1
得分: 8
以下是翻译好的内容:
在 Oracle 文档中有一个页面解释了什么是“捕获通配符”。
在 shuffle
的情况下,你可以将“dump array”操作提取到一个通用的辅助方法中:
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);
这样做的原因是因为如文档中所述:
多亏了这个辅助方法,编译器使用类型推断来确定在调用中 T 是 CAP#1,捕获变量。
英文:
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.
答案2
得分: 2
虽然 Sweeper 已经完美回答了这个问题,但我会在 "原始上下文" 警告上添加一些额外的背景信息,以及开发者可能在那里的意图(免责声明,我不是编写那段代码的人,我只是在猜测)。
根据文档,
> 原始类型是没有任何类型参数的泛型类或接口的名称。
所以在上面的代码中,ListIterator it
是一个原始类型。而且你正在分配list
的ListIterator
,它是通配符类型List<?>
。
现在在上述代码中使用原始类型可能会出现什么问题呢?当你使用原始类型直接调用在相应的泛型类型中定义的泛型方法时,编译器会抛出警告。它会对unchecked invocation
(未经检查的调用)显示警告,因为你绕过了泛型类型检查,这可能会导致类型不匹配的运行时错误。在这种情况下,以下代码正在执行这个操作:
it.set((T)arr[i]);
这里的arr[i]
是从List<?>
创建的通配符类型,但it
是一个原始的ListIterator
。所以按设计,编译器会显示警告。为了消除这个警告,开发者使用了以下方法:
@SuppressWarnings({"rawtypes", "unchecked"})
现在,一个更好的解决方法是创建一个私有的泛型方法来创建泛型的ListIterator
,然后将数组放入其中。但开发者似乎不喜欢那个想法(尽管我认为那样会更清晰)。
英文:
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).
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论