有界类型参数(T 扩展)和上限通配符(? 扩展)之间的差异

我知道已经发布了一个类似的问题,尽管我认为我的问题有些不同......

假设您有两种方法:

// Bounded type parameter
private static <T extends Number> void processList(List<T> someList) {

}

// Upper bound wildcard
private static void processList2(List<? extends Number> someList) {
    // ...
}

据我所知,这两种方法都接受参数,这些参数的类型或子类型为 。ListNumberListNumber

但是这两种方法之间到底有什么区别呢?


答案 1

在编译时,这两种语法之间有几个区别:

  • 使用第一种语法,您可以向其添加元素,但使用第二种语法则不能。这通常被称为PECS,不太常见的称为PUT和GET prinicple。someList
  • 使用第一种语法,你有一个类型参数的句柄,所以你可以用它来做一些事情,比如在类型的方法中定义局部变量,强制转换对类型的引用,调用在表示的类中可用的方法,等等。但是使用第二种语法,您没有对类型的句柄,因此您无法执行任何此操作。TTTT
  • 实际上可以从第二个方法调用第一个方法来捕获通配符。这是通过帮助程序方法捕获通配符的最常见方法。

    private static <T extends Number> void processList(List<T> someList) {
        T n = someList.get(0);
        someList.add(1,n); //addition allowed.   
    }
    
    private static void processList2(List<? extends Number> someList) {
        Number n = someList.get(0);
        //someList.add(1,n);//Compilation error. Addition not allowed.
        processList(someList);//Helper method for capturing the wildcard
    }
    

请注意,由于泛型是编译时糖,因此在更广泛的级别上的这些差异仅限于编译。


答案 2

我可以想到以下差异:

a)修改方法内部的列表,请考虑以下代码:

    // Bounded type parameter
    private static <T extends Number> void processList(List<T> someList)
    {

       
       T t = someList.get(0);
       if ( t.getClass() == Integer.class )
       {
           Integer myNum = new Integer(4);
           someList.add((T) myNum);
       }
       
    }

    // Upper bound wildcard
    private static void processList2(List<? extends Number> someList)
    {
        Object o = someList.get(0);
        if ( o instanceof Integer )
        {
            Integer myNum = new Integer(4);
            someList.add(myNum); // Compile time error !!
        }
    }

使用通配符,您无法向列表中添加元素!编译器告诉您它不知道什么是 .但是在第一种方法中,您可以添加一个 by first check if is ,没有编译时错误。myNumIntegerTInteger

b) 第一种方法称为泛型方法。它遵循为泛型方法定义的语法。方法定义中指定的上限用于限制参数类型。

第二个不一定称为泛型方法,它是碰巧接受泛型参数的普通方法。带关键字的通配符用作放宽方法可以接受的类型的方法。?extends


推荐