Java通配符捕获因未知原因失败

Java通配符捕获因未知原因失败,java,generics,wildcard,static-methods,Java,Generics,Wildcard,Static Methods,在编写静态泛型方法时,执行插入排序。我遇到了以下无法解决的通配符捕获问题。这个问题有一个简单的解决方案,但我仍然想知道为什么我最初的尝试没有编译。我为快速排序做了类似的事情,它可以正确编译 注意:我知道,通过将声明从List更改为短版本:问题在于T key方法的move参数 更深入:你有一个?扩展T通配符和包含该类型的列表。让我们调用实际为U的类型。由于您是以接收列表时的原始类型直接传递列表,因此您正在调用版本的move。这要求您向其传递一个列表-工作正常-和一个U键-但为该键传递的变量类型为T

在编写静态泛型方法时,执行插入排序。我遇到了以下无法解决的通配符捕获问题。这个问题有一个简单的解决方案,但我仍然想知道为什么我最初的尝试没有编译。我为快速排序做了类似的事情,它可以正确编译


注意:我知道,通过将声明从
List更改为短版本:问题在于
T key
方法的
move
参数

更深入:你有一个
?扩展T
通配符和包含该类型的
列表。让我们调用实际为
U
的类型。由于您是以接收列表时的原始类型直接传递列表,因此您正在调用
版本的
move
。这要求您向其传递一个
列表
-工作正常-和一个
U键
-但为该键传递的变量类型为
T
U
被绑定为
T
的子类,因此这是一个不安全的强制转换

您也可以调用
版本的
move
。在这种情况下,
T键
就可以了,但是现在您试图将
列表
作为
列表
传递,这是不安全的强制转换。无论哪种方式,您都有一个不安全的强制转换,因此编译器会报告一个错误

如果消除中间变量并调用
move(j+1,col.get(i),col)
,我相信这会编译,因为编译器可以判断
col.get(i)
的结果是
U
类型,满足
move
的类型签名

为了更明确地说明为什么这是一个问题,让我们看一个例子。我将假装
Number
实现了
compariable
,因为它使事情更容易理解。您可以按如下方式调用您的排序:

ArrayList<Integer> list = new ArrayList<>();
list.add(2);
list.add(1);
insertionSort<Number>(list);
ArrayList list=new ArrayList();
增加(2);
增加第(1)款;
插入排序(列表);
现在,您的代码进入第一个
move
调用。在这一行中,您有:

  • 第一个参数:
    j+1
    ,类型为
    int
  • 第二个参数:
    ele
    ,类型为
    Number

  • 第三个参数:
    col
    ,类型为
    List此编译错误是由生产者扩展、消费者超级原则:PECS引起的

    总之,您的代码既是生产者又是消费者(消费者是
    move
    方法),因此您不能使用
    ?扩展T
    ,因为您不能使用
    扩展
    执行put(在本例中为
    设置

    编辑

    那么,为什么您的快速排序
    swap
    方法可以工作呢

    注意方法签名的差异

    快速排序
    无效交换(int j,int partitionIndex,List col)

    vs

    insertionSort
    voidmove(int-jplus1,T键,列表列){

    快速排序的区别在于您提供了要交换的索引,但在未编译的insertionSort中,您提供了一个类型为
    t

    只需使用索引,您就必须从列表中获取值,因此类型
    T
    是一个全新的推断类型。在insertionSort中,您提供了一个已经获取的
    T
    值,与新的推断类型相比,它是一个错误的捕获类型,而新的推断类型中不能包含
    扩展
    ,因为您既获取又获取滴答声


    如果您将代码更改为仅使用偏移量,它将进行编译,但您必须修改您的方法以进行不同的插入,因为如果您以这种方式更改代码,您将丢失要在列表中较早插入的值。

    交换和移动之间的区别在于,交换不采用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);
    }
    
    公共静态void insertionSort(列表列){
    对于(int i=1;i-1&&col.get(j).compareTo(key)>0){
    T ele=col.get(j);
    移动(j+1,ele,col);//**不编译**
    j=j-1;
    }
    移动(j+1,键,列);//**不编译**
    }
    }
    私有静态无效移动(int-jplus1,T键,列表列){
    col.set(jplus1,键);
    }
    
    我找到了一种解决问题的方法:它需要创建一个额外的方法来正确捕获通配符:

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

    public static出于某种原因,原始消息被略微更改:我试图说,通过更改列表,您不是在几个小时前问了同样的问题吗?不是……昨天是我第一次发布此问题。两者都使用t类型的通用密钥?不确定您在说什么。我很好奇为什么它不起作用?不是r我对主观思考很感兴趣,尽管这种思考很有趣。谢谢你的回答。我仍然不明白,如果我将通配符放在insertionSort声明中,为什么移动不会编译。似乎在上面的quickSort中创建了相同类型的方法,但它编译了!我确实注意到了它们之间的一个区别包含静态方法的类的声明中的quickSort和insertionSort。quickSort有一个类参数,而insertionSort没有。因此我在insertionSort类中放置了一个类参数,但没有用。仍然不会编译。私有静态void move(int jplus1,T key,List col){----交换工作正常,编译正常…两者在结构上是相同的???私有静态无效交换(int j,int partitionIndex,List col){@GeorgeCurington
    move
    swap
    之间的重要区别在于
    move
    在其参数中的两个不同位置有
    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);
    }
    
    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){