Thursday, December 6, 2007

Why does Set.contains() take an Object, not an E?

Virtually everyone learning Java generics is initially puzzled by this. Why should code like the following compile?

Set<Long> set = new HashSet<Long>();
set.add(10L);
if (set.contains(10)) {
// we won't get here!
}

We're asking if the set contains the Integer ten; it's an "obvious" bug, but the compiler won't catch it because Set.contains() accepts Object. Isn't this stupid and evil?

A popular myth is that it is stupid and evil, but it was necessary because of backward compatibility. But the compatibility argument is irrelevant; the API is correct whether you consider compatibility or not. Here's the real reason why.

Let's say you have a method that wants to read from a Set of Foos:

public void doSomeReading(Set<Foo> foos) { ... }

The problem with this signature is it won't allow a Set<SubFoo> to be passed in (where SubFoo is, of course, a subtype of Foo).

To preserve the substitutability principle, any method that wants to read from a set of Foos should be equally able to read from a set of SubFoos, so let's tweak our signature:

public void doSomeReading(Set<? extends Foo> foos) { ... }

Perfect!

But here's the catch: if Set.contains() accepted type E instead of type Object, it would now be rendered completely unusable to you inside this method body!

That signature tells the compiler, "don't let anyone ask about containment of an object unless you are damn sure that it's of the exact right type." But the compiler doesn't know the type -- it could be a Foo, or SubFoo, or SubSubFoo, or who knows what? Thus the compiler would have to forbid everything -- the only safe parameter to a method like this is null.

This is the behavior you want for a method like Set.add() -- if you can't make damn sure of the type, don't allow it. And that's why add() accepts only type E while contains() accepts anything.

So the distinction I'm making is between read methods and write methods, right? No, not exactly -- notice that Set.remove() also accepts Object, and it's a write method. The real difference is that add() can cause "damage" to the collection when called with the wrong type, and contains() and remove() cannot.

Uniformly, methods of the Java Collections Framework (and the Google Collections Library too) never restrict the types of their parameters except when it's necessary to prevent the collection from getting broken.

So what to do about this vexing source of bugs, as illustrated at top? Well, when I typed that code into IntelliJ, it flagged a warning for me right away. This let me know to either fix the problem or add an annotation/comment to suppress it. Problem solved.

Static analysis plays an extremely important role in the construction of bug-free software. And the very best kind of static analysis is the kind that pops up in your face the second you write something questionable.

The moral of the story: if you're not coding in a good, modern IDE, you're coding with one hand tied behind your back!

818 comments:

«Oldest   ‹Older   801 – 818 of 818
Paid Connections said...

Quite insightful! I've got an opportunity to learn.. thank you.
Secure phone billing solutions

Softcrayons Tech Solutions Pvt. Ltd said...


great information. SAP Course in Noida

prachu said...

This post is easily one of the best resources I’ve come across. It’s clear, detailed, and accessible—qualities that are hard to find in one place. Thank you for creating such a valuable guide
https://iimskills.com/data-science-courses-in-westminster/


Mohd Bilal said...

Great Information Provided Thanks For The Information !
Data science Courses in Richmond

Data Science said...

This explanation does a fantastic job of demystifying why Set.contains() accepts an Object rather than E. It highlights the key balance between maintaining type safety and practical usability in the Java Collections Framework. By accepting Object, methods like contains() and remove() maintain flexibility and support polymorphism, aligning with the substitutability principle. The article also wisely points out that robust static analysis tools in modern IDEs can catch potential issues early, underscoring the importance of using these tools to write more reliable code.
Data science courses in Gurgaon

Bhumi IIM Skills said...

Thanks for explaining why set.contains takes an object instead of an e type! Your breakdown of the reasoning behind this design choice is really insightful. It’s always helpful to understand the underlying mechanics of these methods. Looking forward to more of your posts on Java intricacies
Data science courses in Dubai

Vijay said...

An awesome work done very insightful article.
Data science courses in Pune

Data Analytics Courses In Ontario said...

"I really enjoyed reading this! Anyone in Kochi interested in data science should definitely look into the Data Science courses in Kochi for an excellent start to their learning path."

sakshi.gupta.university said...

The `Set.contains()` method takes an `Object` instead of `E` to allow flexibility in comparison. This design enables checking for equality without requiring the argument to be of the same type as the set's elements, leveraging the `equals()` method.

Data science courses in Pune

NEHA PATHARE said...

"Thanks for making this topic accessible to everyone."
Data science course in mumbai.

IIM Skills Data Science said...

Great explanation of why Set.contains takes an object instead of a primitive type! It really clears up the confusion. Thanks for sharing
Data science Courses in Canada

RICHA said...

"Great explanation of why `Set.contains` takes an object rather than a primitive type like `e`! The post does a fantastic job of addressing the nuances of Java's autoboxing and how it impacts the `Set.contains()` method. I especially appreciate the clarity in breaking down the concept, making it easier for developers to understand the reasoning behind it. Thanks for sharing such valuable insights!"
Data science courses in the Netherlands

kriti sharma said...

Great question and a fantastic explanation of why the Set.contains() method uses Object instead of E. It’s always great to learn about the inner workings of Java’s collections framework. Your post clears up some confusion that many developers have about generics in Java.
Data science courses in Glasgow

Abar Singh said...

This blog clarifies why Set.contains uses Object instead of a more specific type.

Data science courses in France

NEEL KBH said...

The reason Set.contains() accepts an Object instead of a type parameter like E is to maintain compatibility with generics' flexibility, allowing a broader range of input types, such as subclasses. While this can cause confusion, it ensures that methods like contains() and remove() remain usable in contexts with wildcard types like Set. By limiting the type restrictions for read operations, Java preserves the substitutability principle without unnecessarily restricting the functionality of the collection.

Data science Courses in City of Westminster

Neel KBH
kbhneel@gmail.com

Shikhaiimskills said...

The Set.contains method in Java takes an Object instead of a generic E to ensure compatibility with any object, not just those of the type parameter E. This allows flexibility, such as checking if a set contains an equivalent object, even if it's not of type E. However, improper use may lead to runtime errors.
Data science Courses in Berlin


iim skills Diksha said...

This post does a great job of demystifying the design choice behind Set.contains() accepting an Object instead of E. It clarifies that the decision is deliberate and rooted in ensuring flexibility and usability of the Collections Framework, rather than backward compatibility alone. Very nice Blog!
Data science Courses in Ireland

AI Readers club said...

This is a thoughtful discussion on the design decisions behind Set.contains in Java. Your explanation of type safety versus flexibility sheds light on an important aspect of API design. Thanks for clarifying this often-overlooked detail!"
Data science courses in UAE
https://iimskills.com/data-science-courses-in-uae/

«Oldest ‹Older   801 – 818 of 818   Newer› Newest»