编写静态泛型方法来执行插入排序。我遇到了以下我无法解决的外卡捕获问题。有一个简单的解决方案,但我仍然想知道为什么我的原始尝试不编译。我为quicksort做了类似的事情,它可以正确编译。
注意:我知道通过将声明从List<? extends T> col
更改为List<T> col
,代码将编译并正常运行。但是因为我在下面的“移动”方法中捕获通配符,所以如果我离开它也应该编译
“?延伸T”原样。有没有人知道为什么它拒绝编译?我在Eclipse(NEON)中使用jdk1.8.0_60。
以下是无法编译的相关代码:
public static <T extends Comparable<? super T>>
void insertionSort(List<? extends T> col) {
for ( int i = 1 ; i < col.size(); i++ ){
int j = i - 1 ;
T key = col.get(i);
while ( j > -1 && col.get(j).compareTo(key) > 0 ) {
T ele = col.get(j);
InsertionSort.move(j+1,ele,col); // **DOES NOT COMPILE**
j = j - 1;
}
InsertionSort.move(j+1, key, col); // **DOES NOT COMPILE**
}
}
private static <T> void move(int jplus1, T key, List<T> col) {
col.set(jplus1, key);
}
我正在使用quickSort做类似的事情,无论信不信,编译正确。 以下是实际正确编译的快速排序:请注意 swap 方法。
public static <T extends Comparable<? super T>>
void quickSort(List<? extends T> col) {
Objects.requireNonNull(col);
_quickSort(col, 0, col.size() - 1);
}
private static <T extends Comparable<? super T>>
void _quickSort(List<? extends T> col, int start, int end) {
int partitionPoint = 0;
if (start < end) {
partitionPoint = partition(col, start, end);
_quickSort(col, start, partitionPoint - 1);
_quickSort(col, partitionPoint + 1, end);
}
}
private static <T extends Comparable<? super T>>
int partition(List<? extends T> col, int start, int end) {
T pivot = col.get(end);
int partitionIndex = start;
for (int j = start; j <= (end - 1); j++) {
int cmp = col.get(j).compareTo(pivot);
if (cmp < 1) {
swap(j, partitionIndex, col); // COMPILES CORRECTLY
partitionIndex++;
}
}
swap(partitionIndex, end, col);
return partitionIndex;
}
private static <T> void swap(int j, int partitionIndex, List<T> col) {
T temp = col.get(j);
col.set(j, col.get(partitionIndex));
col.set(partitionIndex, temp);
}
答案 0 :(得分:1)
简短版本:问题是T key
方法的move
参数。
更深入:所以你有一个? extends T
通配符和一个包含该类型的List
。让我们调用实际类型为U
的任何类型。由于您直接传递列表,因为您收到的是原始类型,因此您调用的<U>
版本为move
。这需要您传递List<U>
- 工作正常 - 和U key
- 但您为密钥传递的变量是T
类型。 U
被绑定为T
的子类,因此这是一个不安全的演员。
您也可以拨打<T>
版move
。在这种情况下,T key
会没问题,但现在您正试图将List<U>
作为List<T>
传递而 是一个不安全的演员。无论哪种方式,您都有不安全的强制转换,因此编译器会报告错误。
如果消除干预变量并调用move(j+1, col.get(i), col)
,我相信会编译,因为编译器可以告诉col.get(i)
的结果是U
类型,满足类型签名move<U>
。
为了更明确地说明为什么这是一个问题,让我们来看一个例子。我假装Number
实施Comparable<Number>
因为它使事情更容易理解。你打电话给你的排序如下:
ArrayList<Integer> list = new ArrayList<>();
list.add(2);
list.add(1);
insertionSort<Number>(list);
现在,您的代码将进入第一个move
调用。在那一行,您有:
j+1
,类型为int
ele
,类型为Number
col
,类型为List<? extends Number>
在这种情况下,? extends Number
恰好是Integer
。
现在,鉴于这些论点,被调用的move
的泛型类型是什么?有两种明显的可能性,move<Integer>
和move<Number>
。
move<Integer>
:这需要第二个T key
类型的参数(Integer
)。您为其提供了Number
类型的变量。这不合适,所以这个选项不起作用。
move<Number>
:第二个参数工作正常,但现在看第三个。第三个参数需要类型List<Number>
。您传递的内容是List<Integer>
。如果编译器允许您这样做,则意味着允许move
方法将3.14159
添加到列表中,这会破坏列表的仅包含的约束整数。因此,这不适合,所以这个选项不起作用。
没有任何其他相关选项需要考虑。所以,编译器到达那一行,试图弄清楚它是否正在调用move<Integer>
,move<Number>
,move<String>
或其他任何东西,并发现没有任何作用。 move
没有任何版本符合您尝试立即传递给它的所有三个参数,因此会抛出错误。
答案 1 :(得分:1)
此编译错误是由Producer Extends,Consumer Super Principle:PECS What is PECS (Producer Extends Consumer Super)?
引起的最重要的是,您的代码既是生产者又是消费者(消费者是move
方法)因此您无法使用? extends T
,因为您无法执行put(或{{ 1}}在这种情况下)使用set
。
修改强>
为什么你的快速排序extends
方法会起作用?
注意方法签名差异
快速排序swap
VS
insertionSort <T> void swap(int j, int partitionIndex, List<T> col)
快速排序的区别在于您将索引交换,但在不编译的insertionSort中,您将其中一个值作为类型<T> void move(int jplus1, T key, List<T> col) {
提供。
只需使用索引,您就必须从列表中获取值,因此类型T
是一种全新的推断类型。在您的insertedSort中,您提供了一个已经获取的T
值,这是一个错误的捕获类型,与新的推断类型相比,因为您正在获取和放置,所以它不能包含T
。
如果你改变你的代码只使用偏移它会编译,但是你必须修改你的方法以不同的方式插入,因为你拥有它的方式如果你改变它就会失去先前插入列表中的值。
答案 2 :(得分:0)
swap和move之间的区别在于swap不接受类型为T的泛型键。这就是编译器所抱怨的内容,如前面的答案中所述。在您的方法中使用通配符根本没有意义。为什么你需要通配符?你不能这样做:
public static <T extends Comparable<T>> void insertionSort(List<T> col) {
for (int i = 1; i < col.size(); i++) {
int j = i - 1;
T key = col.get(i);
while (j > -1 && col.get(j).compareTo(key) > 0) {
T ele = col.get(j);
move(j + 1, ele, col); // **DOES NOT COMPILE**
j = j - 1;
}
move(j + 1, key, col); // **DOES NOT COMPILE**
}
}
private static <T> void move(int jplus1, T key, List<T> col) {
col.set(jplus1, key);
}
答案 3 :(得分:0)
我找到了解决问题的方法:它需要创建一个额外的方法来正确捕获通配符:
public static <T extends Comparable<? super T>> void insertionSort(List<? extends T> col){
_insertionSort(col,1,col.size());
}
public static <T extends Comparable<? super T>> void _insertionSort(List<T> col,int start,int end){