boolean containsAll(Collection <! - ? - > c)vs boolean addAll(Collection <! - ?extends E - > c)的设计决策;在集合框架中

时间:2013-08-15 07:05:18

标签: java generics wildcard

为什么boolean containsAll(Collection&lt;?&gt; c);每种类型允许收集框架的方法。但布尔addAll(收集&lt;?扩展E&gt; 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)中

3 个答案:

答案 0 :(得分:3)

我猜测原因是containsAll(和containsremoveremoveAll)使用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)

原因与方法addcontains相同。

add接受集合的泛型类型的参数,因为只有这样的对象可以添加到集合中。这就是在集合中使用泛型的重点。

contains(以及集合框架中的其他方法,如removeMap.get)接受任何对象作为参数。至少有两个原因。

首先,正如Tobias Brandt所说,可能存在一个完全独立的对象,它们与集合中的对象“相等”(由equals实现定义)。

其次,每个集合Collection<E>都可以被视为Collection<? extends D>,其中D是E的超类,或者甚至是Collection<?>(与Collection<? extends Object>相同) 。如果你进行这个upcast,你不能再调用add方法,因为它的签名看起来像add(?)而且编译器禁止调用它,因为它永远无法确保类型安全(并且你很好)无法调用add,因为您可以向集合中添加错误的类型)。但是,调用contains可能仍然有用,这总是类型安全的,为什么不允许它?为了实现这一点,contains方法需要Object作为参数类型,否则无法调用类似于add

addAllcontainsAll的签名只是遵循同样的原则。