例如:
public String add(Set<?> t){
...;
}
public <T> String add(Set<T> t){
...;
}
第一个使用通配符泛型;第二种是通用方法的正常形式。 有什么区别?
在什么情况下我们需要通配符泛型,而不是普通的泛型?
答案 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){...}
}
然后为Apple
和Banana
创建一个存储库,只需扩展此类并用实际类型替换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
。