列表包含方法非类型安全

2022-09-03 14:57:10

在经历这个问题:“检查对象是否包含在链接列表中”时,我意识到用户正在尝试将字符串参数传递给LinkedList类型的LinkedList的包含方法:

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

    list.contains("String");

这不会引发任何编译错误,因为'contains'方法接受java.lang.Object并允许向其传递字符串值。

因此,出于好奇,我想知道为什么选择这种方法来接受“对象”,而它可以被限制为只接受列表类型(就像添加一样)。这难道不是违背了泛型的整个目的,即“编译时更强的类型检查”吗?


答案 1

假设您有一个标识 by(两者都是类,既不扩展另一个)。BankAccountAccountId

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

现在,可以接受并与帐户的ID进行比较。这样,你将能够调用,即使参数具有与集合的类型完全无关的类型,它也将正常工作。AccountId.equals()BankAccountaccounts.contains(account_id)contains()


答案 2

包含方法的规范说:

当且仅当此集合包含至少一个元素 e 时返回 true,使得 [docs oracle][1](o==null ? e==null : o.equals(e))

但是,它还说,如果集合不允许空元素,它可能会引发 a,或者如果指定元素的类型与此集合不兼容,则可能会引发 a。这些被标记为 。NullPointerExceptionClassCastExceptionoptional

此外,它还说:

集合框架接口中的许多方法都是根据 equals 方法定义的

但:

此规范不应被解释为暗示调用 Collection.contains with a non-null argument o 将导致为任何元素 e 调用 o.equals(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)


推荐