真的需要通配符泛型吗?

时间:2014-04-05 21:42:56

标签: java generics

例如:

    public String add(Set<?> t){
    ...;
    }



    public <T> String add(Set<T> t){
    ...;
    }

第一个使用通配符泛型;第二种是通用方法的正常形式。 有什么区别?

在什么情况下我们需要通配符泛型,而不是普通的泛型?

4 个答案:

答案 0 :(得分:12)

这是需要通配符的情况。此方法采用List<List<?>>,这是一个列表列表。该方法可以将不同组件类型的列表添加到其中:

public void foo(List<List<?>> t) {
    t.add(new ArrayList<String>());
    t.add(new ArrayList<Integer>());
}

如果没有通配符,则无法使用泛型类型参数执行此操作。例如,以下内容不起作用:

public <T> void foo(List<List<T>> t) {
    t.add(new ArrayList<String>()); // does not compile
    t.add(new ArrayList<Integer>()); // does not compile
}

答案 1 :(得分:11)

由于添加了对泛型的支持,因此使用参数化类型而不提供类型参数通常会导致编译器警告。另一方面,在某些情况下,您根本不关心类型参数是什么(即您不在任何地方使用该类型),或者更糟糕的是,您可能不知道{{1}完全没有问题,并且使用T可以让您在不引起编译器警告的情况下表达它。

&#34;无需照顾的可能用例&#34;案例(为简洁起见非常简单,但你明白了):

<?>

不知道&#34>的例子。 case:来自public void clearList(List<?> list) { list.clear(); } 类的实际方法签名:

Class

此处该方法返回某种static Class<?> forName(String className); 类型的对象。 Class是通用的,但当然您不知道类型,因为它取决于在运行时解析的Class参数。所以你不能把className放在这里因为T在编译时是不可知的(即使对于一个特定的调用站点),并且只使用没有类型参数的T会很糟糕练习并引发编译器警告。

答案 2 :(得分:1)

通配符表单是指您不介意处理的对象类型。

泛型表单允许您添加处理对象类型的约束。

示例用例可能如下: 使用添加/更新/删除方法的通用存储库,您可以使用泛型类型定义常见行为:

public class Repository<T>{
 public void add(T t){...}
 public void update(T t){...}
 public void remove(T t){...}
}

然后为AppleBanana创建一个存储库,只需扩展此类并用实际类型替换T:

public class AppleRepo extends Repository<Apple> {}
public class BananaRepo extends Repository<Banana> {}

如果通用存储库被声明为Repository<?>,那就不太好了,因为它不仅限于Banana,如果没有强制转换对象,你将无法在其中使用Banana特定方法;

此外,泛型允许您表达进一步的约束,例如

Repository<T extends Fruit>

允许您将通用存储库类限制为水果。您将能够在其代码中调用Fruit方法。

答案 3 :(得分:0)

调用方法没有区别。

在第二种方法(add(Set<T>))中,您可以创建T类型的变量:

public <T> String add(Set<T> t){
    T item = t.iterator().next();
    //....
}

这会给你一些额外的类型检查。 在第一种方法中,您只需使用Object