列表包含不安全的方法

时间:2014-09-21 06:12:10

标签: java generics

在回答这个问题时:" Checking if an object is contained within a linkedlist",我意识到用户正在尝试将String参数传递给LinkedList类型的LinkedList的contains方法:

    LinkedList<LinkedList> list = new LinkedList<LinkedList>();

    list.contains("String");

这不会引发任何编译错误,因为&#39;包含&#39;方法接受java.lang.Object并允许将字符串值传递给它。

所以出于好奇,我想了解为什么选择这种方法来接受&#39;对象&#39;当它被限制为只接受列表类型时(就像添加一样)。是不是它打败了泛型的整个目的,即在编译时加强类型检查&#34;?

5 个答案:

答案 0 :(得分:8)

假设您有一个由BankAccount标识的AccountId(两个都是类,不会扩展另一个)。

现在我们假设您有LinkedList<BankAccount> accounts;,并希望查看是否有具有指定ID的帐户。

现在,AccountId.equals()可以接受BankAccount并将自己与帐户的ID进行比较。有了这个,你就可以调用accounts.contains(account_id),即使contains()的参数的类型与集合的类型完全无关,它也会正常工作。

答案 1 :(得分:4)

contains方法的规范说:

  当且仅当此集合包含至少一个元素e时,

才返回true (o==null ? e==null : o.equals(e))   [docs oracle] [1]

但是,它还说如果集合不允许null元素,它可能抛出NullPointerException,如果指定元素的类型与此集合不兼容,它可能抛出ClassCastException。这些标记为optional

此外,它还说:

  

Collections Framework接口中的许多方法都是根据equals方法

定义的

但:

  

不应将此规范解释为暗示使用非null参数o调用Collection.contains将导致为任何元素调用o.equals(e)e

因此,我的结论是,这是允许实现定义不同行为(例如接受空元素)和优化(例如,重写类的equals方法,以便您可以检查集合中是否包含元素)的某种黑客没有提及它。)

我将用一个例子来解释最新的内容:

public class A {

   public void initialize() {
      // Lots of code and heavy initialization
   }

   public String id;

   @Override
   public int hashCode() {
     return id.hashCode();
   }

   @Override
   public boolean equals(Object o) {
      return this.hashCode() == o.hashCode();
   }
}

然后:

SomeCollection<A> collection = new SomeCollection<A>(); 

// Create an element and add it to the collection
A a = new A();
a.initialize(); // Heavy initialization
element.id = "abc";
collection.add(a);

// Check if the collection contains that element

// We create a second object with the same id, but we do not initialize it
A b = new A();
b.id = "abc";

// This works for many common collections (i.e. ArrayList and HashSet)
collection.contains(b);

实际上,有更多方法,例如indexOf(Object o)remove(Object o)。所以我认为这不是为了兼容性,而是为了实现这种解决方案而设计的。

http://docs.oracle.com/javase/7/docs/api/java/util/Collection.html#contains(java.lang.Object)

答案 2 :(得分:3)

简单... LinkedList实际上是一个Object。

此外,

假设您有每个链接列表的地图:

  • CustomMapItem 1:LinkedList 1
  • CustomMapItem 2:LinkedList 2
  • CustomMapItem 3:LinkedList 3

由于contains()将Object作为参数,因此您可以为CustomMapItem设置equals()hashcode()方法,以便找到相应的LinkedList。

答案 3 :(得分:1)

首先,contains方法不会修改底层集合,因此它没有像add方法那样的严格要求。

更重要的是,包含对象相等的处理。它实际上在列表的元素上调用equals方法。似乎两个对象相等,它们至少需要相同的类型,但实际情况并非如此。这完全取决于你的equals方法的定义。

一个例子:

List<A> myList = new LinkedList<A>();
...
B b = new B();
myList.contains(b); //expected true if 'value' attribute is equal.


class A {
     int value;
     public boolean equals(Object object){
        if(object instanceof A && ((A)object).getValue() == this.value)
        return true;
        else if(object instanceof B && ((B)object).getValue() == this.value)
        return true;
        else
        return false;

}

同样

 class B {
     int value;
     public boolean equals(Object object){
        if(object instanceof A && ((A)object).getValue() == this.value)
        return true;
        else if(object instanceof B && ((B)object).getValue() == this.value)
        return true;
        else
        return false;

}

答案 4 :(得分:-1)

只是为了向后兼容。尽管泛型可用于将集合的内容限制为同类型,但如果您愿意,使集合包含异构对象仍然是一个有效的用例。

public static void main(String[] args)
{
    List<String> homogeneousStringList = new ArrayList<String>();

    homogeneousStringList.add("Foo");
    homogeneousStringList.add("Bar");

    List heterogeneousObjectList = new ArrayList();

    heterogeneousObjectList.add("Foo");
    heterogeneousObjectList.add(Integer.valueOf(1));
    heterogeneousObjectList.add(new Date());

    System.out.println(homogeneousStringList.toString());
    System.out.println(heterogeneousObjectList.toString());
}

产生以下输出:

[Foo,Bar]

[Foo,1,Sun Sep 21 00:36:59 MDT 2014]

相关问题