2013-02-28 109 views
6

因此,我正在阅读泛型以重新熟悉这些概念,特别是在涉及通配符的地方,因为我很少使用它们或碰到它们。从我所做的阅读中,我无法理解他们为什么使用通配符。我不断遇到的一个例子如下。Java泛型:通配符

void printCollection(Collection<?> c) { 
    for (Object o : c){ 
    System.out.println(o); 
    } 
} 

为什么你会不会写为:

<T> void printCollection(Collection<T> c) { 
    for(T o : c) { 
     System.out.println(o); 
    } 
} 

从Oracle网站又如:

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

这是为什么不写成

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

上午我错过了什么?

+0

可能[duplicate](http://stackoverflow.com/questions/10943137/difference-between-generic-type-and-wildcard-type) – Jayamohan 2013-02-28 01:08:22

+1

@Jayamohan我不同意。 – 2013-02-28 01:10:04

回答

3

Oracle

一个问题是出现的是:当我应该使用泛型方法,当我应该使用通配符类型?为了理解答案,我们来看一下Collection库中的几个方法。

interface Collection<E> { 
    public boolean containsAll(Collection<?> c); 
    public boolean addAll(Collection<? extends E> c); 
} 

我们可以用通用的方法,而不是在这里:

interface Collection<E> { 
    public <T> boolean containsAll(Collection<T> c); 
    public <T extends E> boolean addAll(Collection<T> c); 
    // Hey, type variables can have bounds too! 
} 

然而,在这两个containsAll和中的addAll中,类型参数T只能使用一次。返回类型不依赖于类型参数,也没有任何其他方法的参数(在这种情况下,只有一个参数)。这告诉我们,类型参数被用于多态;它的唯一影响是允许在不同的调用站点上使用各种实际参数类型。如果是这样的话,应该使用通配符。通配符旨在支持灵活的子类型,这正是我们要在这里表达的内容。

因此,第一个例子是因为操作不依赖于类型。

第二,这是因为它只取决于Number类。

+0

谢谢,所有的解释都很好,但是这个由于某种原因帮助我最了解它。 – 2013-02-28 13:49:08

3

为什么使事情比他们需要更复杂?这些示例演示了the simplest thing that could possibly work - 这些示例并不试图说明泛型方法。

为什么你会不会写为:

<T> void printCollection(Collection<T> c) { 
    for(T o : c) { 
     System.out.println(o); 
    } 
} 

因为System.out.println()可以接受的对象,因此需要更具体的什么都没有。

这是为什么不写成

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

再次,因为你并不需要一个不同的参数为每一个不同的T extends Number。接受List<? extends Number>的非通用方法就足够了。

+0

我想我可能只是误解了两者之间的区别,还是他们是一样的?用T而不是用T来解决我将会遇到的情况是什么?我有一种感觉,我完全忽略了通配符的要点。 – 2013-02-28 01:14:08

+2

@JonTaylor:真的没有任何情况下你不能用'T'而不是''来解决,但问题是'''不需要名字并且表示你真的不在乎类型是什么。 – 2013-02-28 01:30:53

+0

@LouisWasserman'Class.forName'返回'Class '可能只是一个适用的通配符的例子。 – 2013-02-28 02:28:44

3

确实,如果方法参数类型具有带有上限的第一级通配符,则它可以由类型参数替换。

计数器的例子(通配符不能由类型参数被替换)

List<?> foo() // wildcard in return type 

    void foo(List<List<?>> arg) // deeper level of wildcard 

    void foo(List<? super Number> arg) // wildcard with lower bound 

现在对于箱子可由任一通配符或类型参数

 void foo1(List<?> arg) 

    <T> void foo2(List<T> arg) 

人们普遍认为,可以解决foo1()foo2()更时尚。这可能有点主观。我个人认为foo1()签名更容易理解。而且业内也普遍采用foo1(),所以最好遵循这个惯例。

foo1()也对待arg更抽象一点,因为你不能容易地在foo1()arg.add(something)。当然,这可以很容易解决(即通过arg到foo2()!)。公共方法看起来像foo1()也是一种常见做法,它在内部前往私人foo2()

还有一些情况下,通配符不会做,需要一个类型参数:

<T> void foo(List<T> foo1, List<T> foo2); // can't use 2 wildcards. 

这个讨论至今大约是方法签名。在其他地方,通配符在不能引入类型参数的情况下是不可或缺的。

+0

+1提到的类型参数[不能有下界](http://stackoverflow.com/questions/2800369/bounding-generics-with-super-keyword)。 – 2013-02-28 02:32:58