Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why does using Collections.emptySet() with generics work in assignment but not as a method parameter?

So, I have a class with a constructor like this:

public FilterList(Set<Integer> labels) {     ... } 

and I want to construct a new FilterList object with an empty set. Following Joshua Bloch's advice in his book Effective Java, I don't want to create a new object for the empty set; I'll just use Collections.emptySet() instead:

FilterList emptyList = new FilterList(Collections.emptySet()); 

This gives me an error, complaining that java.util.Set<java.lang.Object> is not a java.util.Set<java.lang.Integer>. OK, how about this:

FilterList emptyList = new FilterList((Set<Integer>)Collections.emptySet()); 

This also gives me an error! Ok, how about this:

Set<Integer> empty = Collections.emptySet(); FilterList emptyList = new FilterList(empty); 

Hey, it works! But why? After all, Java doesn't have type inference, which is why you get an unchecked conversion warning if you do Set<Integer> foo = new TreeSet() instead of Set<Integer> foo = new TreeSet<Integer>(). But Set<Integer> empty = Collections.emptySet(); works without even a warning. Why is that?

like image 260
Karl von L Avatar asked Jun 17 '10 13:06

Karl von L


People also ask

What is the use of collections emptySet?

The emptySet() method is used to get the set that has no elements. This method is used in set collection. A set is a data structure that can store unique elements.

What is collection emptySet?

The emptySet() method of Java Collections class is used to get the Set that has no elements. These empty Set are immutable in nature.


1 Answers

The short answer is - that's a limitation of the type inference in Java's generic system. It can infer generic types against concrete variables, but not against method parameters.

I suspect this is because methods are dispatched dynamically depending on the runtime class of the owning object, so at compile time (when all generic information is resolved) you can't actually know for sure what the class of the method parameter will be and hence can't infer. Variable declarations are nice and constant, so you can.

Someone else might be able to give more detail and/or a nice link. :-)

In any case, you can always specify the type parameters explicitly for generic calls like so:

Collections.<Integer>emptySet(); 

or even several parameters at once, e.g.

Collections.<String, Boolean>emptyMap(); // Returns a Map<String, Boolean> 

This often looks a little cleaner than having to cast, in cases where inference doesn't kick in.

like image 133
Andrzej Doyle Avatar answered Sep 23 '22 17:09

Andrzej Doyle