如何使用“补充私有方法”来避免使用原始类型?

huangapple go评论71阅读模式
英文:

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({&quot;rawtypes&quot;, &quot;unchecked&quot;})
public static void shuffle(List&lt;?&gt; list, Random rnd) {
    int size = list.size();
    if (size &lt; SHUFFLE_THRESHOLD || list instanceof RandomAccess) {
        for (int i=size; i&gt;1; i--)
            swap(list, i-1, rnd.nextInt(i));
    } else {
        Object arr[] = list.toArray();

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

        // Dump array back into list
        // instead of using a raw type here, it&#39;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&lt;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 &lt;T&gt; void dumpArray(Object[] arr, List&lt;T&gt; list) {
    ListIterator&lt;T&gt; it = list.listIterator();
    for (int i=0; i&lt;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 是一个原始类型。而且你正在分配listListIterator,它是通配符类型List&lt;?&gt;

现在在上述代码中使用原始类型可能会出现什么问题呢?当你使用原始类型直接调用在相应的泛型类型中定义的泛型方法时,编译器会抛出警告。它会对unchecked invocation(未经检查的调用)显示警告,因为你绕过了泛型类型检查,这可能会导致类型不匹配的运行时错误。在这种情况下,以下代码正在执行这个操作:

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

这里的arr[i]是从List&lt;?&gt;创建的通配符类型,但it是一个原始的ListIterator。所以按设计,编译器会显示警告。为了消除这个警告,开发者使用了以下方法:

@SuppressWarnings({&quot;rawtypes&quot;, &quot;unchecked&quot;})

现在,一个更好的解决方法是创建一个私有的泛型方法来创建泛型的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&lt;?&gt;.

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&lt;?&gt; 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({&quot;rawtypes&quot;, &quot;unchecked&quot;})

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).

huangapple
  • 本文由 发表于 2020年9月3日 10:30:46
  • 转载请务必保留本文链接:https://go.coder-hub.com/63715953.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定