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

So, I have a class with the following constructor:

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

and I want to build a new FilterList object with an empty set. Following the advice of Joshua Bloch in my book Effective Java, I do not want to create a new object for an empty set; I just use Collections.emptySet() instead:

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

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

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

It 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, so you get a warning about unverified conversions if you do Set<Integer> foo = new TreeSet() instead of Set<Integer> foo = new TreeSet<Integer>() . But Set<Integer> empty = Collections.emptySet(); works even without warning. Why is this?

+47
java collections generics type-inference
Jun 17 '10 at 13:05
source share
4 answers

The short answer is to restrict type inference in a common Java system. It can invoke generic types against specific variables, but not against method parameters.

I suspect that this is due to the fact that methods are dynamically dispatched depending on the class of the runtime environment of the owner object, so at compile time (when all general information is resolved) you cannot know exactly what the class of the method parameter will be and therefore not can be deduced. Variable declarations are good and constant, so you can.

Someone else can provide more details and / or a good link. :-)

In any case, you can always specify type parameters explicitly for general calls, for example:

 Collections.<Integer>emptySet(); 

or even several parameters at the same time, for example

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

This often looks a little cleaner than throwing, in cases where the output does not work.

+105
Jun 17 '10 at 13:08
source share

try

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

You can force a type parameter for methods that have them, in cases where the output is not good enough, or so you can use subtypes; eg:

 // forces use of ArrayList as parameter instead of the infered List List<String> l = someObject.<ArrayList<String> methodThatTakesTypeParamForReturnType(); 
+6
Jun 17 '10 at 13:08
source share

You want to do this:

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

This tells the emptySet method that its generic parameter must explicitly point to Integer instead of the standard Object . And yes, the syntax is completely funky and unintuitive for that. :)

+5
Jun 17 '10 at 13:07
source share

Java has an output type, it is quite limited. If you are interested in knowing exactly how it works and what its limitations are, it is really nice to read:

http://www.angelikalanger.com/GenericsFAQ/JavaGenericsFAQ.html#Type%2BArgument%2BInference

+5
Jun 17 '10 at 13:10
source share



All Articles