【问题标题】:Design decision of boolean containsAll(Collection<?> c) vs boolean addAll(Collection<? extends E> c); in collection framework [duplicate]boolean containsAll(Collection<?> c) vs boolean addAll(Collection<? extends E> c); 的设计决策在收集框架中[重复]
【发布时间】:2013-08-15 07:05:18
【问题描述】:

为什么 boolean containsAll(Collection c);每种类型 ? 都允许使用收集框架的方法。但是 boolean addAll(Collection 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。用 boolean containsAll(Collection c 替换 boolean containsAll(Collection c) 是否有问题)

【问题讨论】:

  • Equals 和 hashCode 是 Object 中的方法,即 >。当您搜索包含某些内容的集合时,您不希望每次都将其转换为适当的类型。
  • @artbristol:是的,这本质上是同一个问题,但有更好的答案。我会投票将其标记为重复。
  • 如果我必须将我的 Collection> 的内容复制到 Collection 以便我可以调用 containsAll()。它本质上等同于接受 Object 类型参数的 equals() 方法。 (好的,通过类型擦除,两个 Collections 都只是 Collection 并且我可以强制转换它,但这只是一个 hack,我们应该编写我们的代码,就好像它不起作用一样。)

标签: java generics wildcard


【解决方案1】:

我猜原因是containsAll(和containsremoveremoveAll)使用Object#equals 进行比较。

可以想象,您可以在 E 中有一个覆盖的 Object#equals 方法,该方法可以为某些不相关类的对象返回 true。并不是说这是一个好主意,但它可能是一个有效的实现。

【讨论】:

  • 这有时是个好主意。例如,java.util.List 要求 .equals() 对于具有相同内容的列表返回 true,即使对于不同的列表类也是如此。
【解决方案2】:

这并不是为了节省 cpu 滴答声。泛型由编译器erased 替换为强制类型转换。

对于addAll 方法类型安全需要考虑。应该只允许用户将Collection&lt;E&gt;E 的某个子类添加到Collection&lt;E&gt;

如果你查看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 调用它,并且没有ClassCastExceptions 的风险。通过避免使用泛型,编译器可以避免添加强制类型转换。

【讨论】:

  • 我认为这与“cpu ticks”无关。
  • @assylias Casting 不是免费的。但它也确实具有提供更好的接口规范的优势。
  • 选角可能不是免费的,但我认为这与课程的设计无关。
  • 这是一条指令 (checkcast)。我认为这不重要。
  • 更重要的是:转换为ˋEˋ 是未经检查的转换,仅在运行时检查ˋObjectˋ。因此,在这种情况下,它是无操作的,它是免费的!所以这当然不是设计决定背后的原因。我怀疑原因与向后兼容性有关。
【解决方案3】:

原因与addcontains方法相同。

add 接受集合的泛型类型的参数,因为只有这样的对象才能添加到集合中。这就是在集合中使用泛型的全部意义所在。

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

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

其次,每个集合Collection&lt;E&gt; 都可以看作Collection&lt;? extends D&gt;,其中D 是E 的超类,甚至可以看作Collection&lt;?&gt;(与Collection&lt;? extends Object&gt; 相同)。如果你做这个向上转换,你不能再调用 add 方法,因为它的签名看起来像 add(?) 并且编译器禁止调用它,因为它永远不能确保类型安全(你不能调用它很好add 因为您可能会向集合中添加错误的类型)。但是,调用contains 可能仍然有用,而且这始终是类型安全的,那么为什么不应该允许呢?为了实现这一点,contains 方法需要有Object 作为参数类型,否则不能像add 那样调用。

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

【讨论】:

  • “但是,调用 contains 可能仍然有用,这始终是类型安全的”但这又是因为 Object.equals() 接受所有类型。如果Object.equals() 不接受所有类型,那么它并不总是类型安全的。
猜你喜欢
  • 2011-05-13
  • 1970-01-01
  • 2017-07-03
  • 1970-01-01
  • 1970-01-01
  • 2012-05-07
  • 2015-12-28
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多