Java泛型类型:List <? extends Number>和List <T extends Number>之间的区别

27

Java泛型类型:什么是两者之间的区别

(1) List <? extends Number>   
(2) List <T extends Number>

据我理解

(1) List <? extends Number> 是一个"未知"数据类型的只读列表,其超类为 "Number"。我们只能读取元素,但无法添加。

(2) List <T extends Number> 是一个具有超类 "Number" 的数据类型列表。我们可以读取和添加元素到列表中。

请参见以下代码示例

class TestGen{

    public static void main(String[] args) {
        double result = 0.0;

        List<Integer> intList = new ArrayList<Integer>();
        intList.add(10);
        intList.add(20);
        intList.add(30);

        result = TestGen.sumOfList1(intList);
        System.out.println("Result=" + result);
        result = TestGen.sumOfList2(intList);
        System.out.println("Result=" + result);
    }

    public static double sumOfList1(List<? extends Number> list) {
        double s = 0.0;
        for (Number n : list)
            s += n.doubleValue();
        return s;
    }

    public static <T extends Number> double sumOfList2(List<T> list) {
        double s = 0.0;

        // getting error while trying to add new element 
        // list<T> is not applicable for argument(Integer) : Why ?
        list.add(new Integer(40));

        for (Number n : list)
            s += n.doubleValue();

        return s;
    }
}

当我尝试将整数(甚至是数字对象)添加到sumOfList2中时,会出现错误。请解释这里有什么问题?
5个回答

29

基本区别在于如果你使用T extends Number,那么你可以引用类型T
list.add((T) new Integer(40));

而如果你使用? extends Number,那么你不能引用类型,但是你仍然可以这样说:
((List<Integer>)list).add((int) s);


不确定是谁给投了反对票...这个解决方案可行,你能否请说明为什么需要进行强制类型转换。 list.add((T) new Integer(40)); 因为我们只添加子类对象(Integer是Number的子类)。 - Rakesh Soni
当你将其转换为 T 时,我猜它会给你一个 类型安全 警告。 - Georgian
我猜,根据参数的类型,List可以接受类型为T的值,因此您需要将参数强制转换为T以添加到add()中。 - venkat balabhadra

13
在隔离的情况下,没有太大的区别。然而,在单个上下文中使用两个List<? extends Number>实例是完全不相关的,而在单个上下文中使用两个List<T extends Number>实例则指向相同的T和相同的接口。
public void addAll(List<? extends Number> to, List<? extends Number> from) {
    for (Number n: from) {
        to.add(n);
    }
}

这种方法失败的原因是因为无法将n添加到to中,而且还因为fromto的成员类型可能完全不同而失败。

public <T> void addAll(List<T extends Number> to, List<T extends Number> from) {
    for (T n: from) {
        to.add(n);
    }
}

这个方法编译没有问题。虽然不是必需的,因为Collections有更好的版本,但它可以正常运行。


3
事实上,addAll(List<T extends Number> to, ...)甚至无法编译通过。正确的方式应该是<T extends Number> void addAll(List<T> to, ...)。请注意不改变原意,并使翻译更加通俗易懂。 - Tavian Barnes

1
sumOfList2中,TNumber的一个具体子类,但它可以是其中任何一个。例如,T可以是BigDecimal,而你不能将Integer添加到List<BigDecimal>中。
如果你想要能够将任何类型的数字添加到列表中,它必须是一个List<Number>
public static double sumOfList2(List<Number> list) {...}

0

1)

List <? extends Number> 

使用它是合适的。

List<Integer> list = Arrays.asList(0,1,2,3,4);

不会抛出错误的代码。

List <Double> list = Arrays.asList(0.0,0.1,0.2,0.3,0.4);

然后您可以将其添加到

result = TestGen.sumOfList1(list);

2) 列表

List<Integer> list = new ArrayList<Integer>();
list.add(0);
list.add(1); Would do the job.

0

第一种情况非常简单,它是从 T extends Number 中知道的。T 是一个对象,它是 Number 类或其子类的类型。 这意味着如果我们有一个使用 T 作为 Number 类型的方法,我们可以相应地操作我们的方法。

<T extends Number> void myMethod ( List <T> num){
     num.add(1); // 1 is an Integer
     num.add(2.3) // 2.3 is a Double
} 

但在这种野字符“?”的情况下,我们不知道将引用哪种类型的对象,即使它扩展了Number,但它可以是任何类型的对象。

void myMethod( List <? extends Number> num){
     // num.add(1); // not a valid statement, as we dont know if '1' is of the type '?'
     // num.add(2.3)  // not a valid statement, as we dont know if '2.3' is of the type '?'
}

所以在这种语句中,唯一可以写入的值就是null,因为任何类型的对象都可以是null。

    void myMethod( List <? extends Number> num){
      num.add(null)
    }

这些方法是只读方法。


1
你的第一个样本实际上无法编译。 - yuxh

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接