Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

List contains method not type safe [duplicate]

Tags:

java

generics

While going through this question: "Checking if an object is contained within a linkedlist", I realized that user was trying to pass a String argument to the contains method of a LinkedList of type LinkedList:

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

    list.contains("String");

This doesn't throw any compilation error because the 'contains' method accepts java.lang.Object and passing a string value to it is allowed.

So just out of curiosity, I wanted to understand why was this method chosen to accept 'Object', when it could have been restricted to accept only the list type (just like add). Isn't it defeating the whole purpose of Generics i.e. "Stronger type checks at compile time"?

like image 299
Gaurav Avatar asked Sep 21 '14 06:09

Gaurav


3 Answers

Let's say you have a BankAccount identified by AccountId (both are classes, neither extending the other).

Let's now say you have LinkedList<BankAccount> accounts; and want to see if there's an account with a given ID.

Now, AccountId.equals() could accept BankAccount and compare itself against the account's ID. With this, you'll be able to call accounts.contains(account_id) and it'll just work even though the argument to contains() has a type that's completely unrelated to the collection's type.

like image 119
NPE Avatar answered Oct 23 '22 18:10

NPE


The specification for the contains method says:

returns true if and only if this collection contains at least one element e such that (o==null ? e==null : o.equals(e)) [docs oracle][1]

However, it also says that it might throw a NullPointerException if the collection doesn't permit null elements or a ClassCastException if the type of the specified element is incompatible with this collection. These are marked as optional.

Furthermore, it also says that:

Many methods in Collections Framework interfaces are defined in terms of the equals method

but:

This specification should not be construed to imply that invoking Collection.contains with a non-null argument o will cause o.equals(e) to be invoked for any element e

Therefore, my conclusion is that this is some kind of hack to allow implementations define different behaviours (e.g. accepting null elements) and optimizations (e.g. overriding the equals method of a class so you can check if an element is contained in the collection without having a reference to it).

I'll explain the latest with an example:

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();
   }
}

And then:

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);

Actually, there are more methods like indexOf(Object o) and remove(Object o) which follow this. So I don't think it's for compatibility, but it's made on purpose to allow for this kind of solutions.

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

like image 33
FranMowinckel Avatar answered Oct 23 '22 20:10

FranMowinckel


Simple... LinkedList is actually an Object.

Furthermore,

Say you have a map for each of your linked list:

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

Because contains() takes an Object as a parameter, you are able to set your equals() and hashcode() methods for CustomMapItem so that you can locate your corresponding LinkedList.

like image 3
shinjw Avatar answered Oct 23 '22 20:10

shinjw