为什么boolean containsAll(Collection<?> c);每种类型?允许收集框架的方法。但布尔addAll(收集<?扩展E> c); 允许?延伸E 。所以,我写了一个澄清程序。 这是我的程序
public class ContainAllTest {
// take ServiceDto
ArrayList<ServiceDto> resultList = new ArrayList<ServiceDto>();
void Test() {
ServiceDto serviceDto = new ServiceDto();
serviceDto.setName("test");
resultList.add(serviceDto);
// another arraylist that takes String
ArrayList<String> resultList1 = new ArrayList<String>();
resultList1.add("test");
// no error, goes for run time.Contain all checking is done for two generic type ServiceDto and String:
resultList.containsAll(resultList1);
// error shown at compile time,as addAll take ServiceDto as generic type but the generic type for resultList1 take String:
resultList.addAll(resultList1);
}
所以,我的问题是我何时可以获得resultList.containsAll(resultList1)的优势;当泛型类型不同时。在我的情况下,String和ServiceDto.Was将布尔containsAll(Collection&lt;?&gt; c)替换为布尔containsAll(Collection&lt;?extends E&gt; c)中
答案 0 :(得分:3)
我猜测原因是containsAll
(和contains
,remove
,removeAll
)使用Object#equals
进行比较。
可以想象,您可以在Object#equals
中使用覆盖E
方法,该方法可以为某些不相关类的对象返回true
。并不是说这是一个好主意,但它可能是一个有效的实现。
答案 1 :(得分:2)
这不是为了节省cpu ticks的优势。编译器将泛型erased替换为强制转换。
对于addAll
方法类型,需要考虑安全性。系统只允许用户将Collection<E>
或某些E
的子类添加到Collection<E>
。
如果查看AbstractCollection
的源代码,您会看到以下方法:
public boolean addAll(Collection<? extends E> c) {
boolean modified = false;
for (E e : c)
if (add(e))
modified = true;
return modified;
}
编译时会看起来像(
)public boolean addAll(Collection c) {
boolean modified = false;
for (Object e : c)
if (add((E)e))
modified = true;
return modified;
}
即。要添加的集合中的每个元素都需要在添加之前从Object
转换为E
。
对于containsAll
方法,无关紧要。由于equals
方法定义为equals(Object other)
,您可以使用任何其他Collection
安全地调用它,并且不存在ClassCastException
的风险。通过避免使用泛型,编译器可以避免添加强制转换。
答案 2 :(得分:1)
原因与方法add
和contains
相同。
add
接受集合的泛型类型的参数,因为只有这样的对象可以添加到集合中。这就是在集合中使用泛型的重点。
contains
(以及集合框架中的其他方法,如remove
和Map.get
)接受任何对象作为参数。至少有两个原因。
首先,正如Tobias Brandt所说,可能存在一个完全独立的对象,它们与集合中的对象“相等”(由equals
实现定义)。
其次,每个集合Collection<E>
都可以被视为Collection<? extends D>
,其中D是E的超类,或者甚至是Collection<?>
(与Collection<? extends Object>
相同) 。如果你进行这个upcast,你不能再调用add
方法,因为它的签名看起来像add(?)
而且编译器禁止调用它,因为它永远无法确保类型安全(并且你很好)无法调用add
,因为您可以向集合中添加错误的类型)。但是,调用contains
可能仍然有用,这总是类型安全的,为什么不允许它?为了实现这一点,contains
方法需要Object
作为参数类型,否则无法调用类似于add
。
addAll
和containsAll
的签名只是遵循同样的原则。