Reputation: 4580
Why is the compiler unable to infer the correct type for the result from Collections.emptySet() in the following example?
import java.util.*;
import java.io.*;
public class Test {
public interface Option<A> {
public <B> B option(B b, F<A,B> f);
}
public interface F<A,B> {
public B f(A a);
}
public Collection<String> getColl() {
Option<Integer> iopt = null;
return iopt.option(Collections.emptySet(), new F<Integer, Collection<String>>() {
public Collection<String> f(Integer i) {
return Collections.singleton(i.toString());
}
});
}
}
Here's the compiler error message:
knuttycombe@knuttycombe-ubuntu:~/tmp/java$ javac Test.java
Test.java:16: <B>option(B,Test.F<java.lang.Integer,B>) in
Test.Option<java.lang.Integer> cannot be applied to (java.util.Set<java.lang.Object>,
<anonymous Test.F<java.lang.Integer,java.util.Collection<java.lang.String>>>)
return iopt.option(Collections.emptySet(), new F<Integer, Collection<String>>() {
^
1 error
Now, the following implementation of getColl() works, of course:
public Collection<String> getColl() {
Option<Integer> iopt = null;
Collection<String> empty = Collections.emptySet();
return iopt.option(empty, new F<Integer, Collection<String>>() {
public Collection<String> f(Integer i) {
return Collections.singleton(i.toString());
}
});
}
and the whole intent of the typesafe methods on Collections is to avoid this sort of issue with the singleton collections (as opposed to using the static variables.) So is the compiler simply unable to perform inference across multiple levels of generics? What's going on?
Upvotes: 3
Views: 1208
Reputation: 18013
First you can narrow down your problem to this code:
public class Test {
public void option(Collection<String> b) {
}
public void getColl() {
option(Collections.emptySet());
}
}
This does not work, you need a temporary variable or else the compiler cannot infer the type. Here is a good explanation of this problem: Why do temporary variables matter in case of invocation of generic methods?
Upvotes: 5
Reputation: 17718
Collections.emptySet()
is not a Collection<String>
unless Java knows that it needs a Collection<String>
there. In this case, it appears that the compiler is being somewhat silly about the order that it tries to determine types, and tries to determine the return type of Collections.emptySet()
before trying to determine the intended template parameter type for B is actually String
.
The solution is to explictly state that you need Collections.<String>emptySet()
, as mentioned by GaryF.
Upvotes: 1
Reputation: 3152
It looks like a typecasting issue - i.e., that it's being required to cast Object
(in Set<Object>
, which would be the type of the empty set) to String
. Downcasts are not, in the general case, safe.
Upvotes: 0
Reputation: 24330
Java needs a lot of hand holding with its inference. The type system could infer better in a lot of cases but in your case the following will work:
print("Collections.<String>emptySet();");
Upvotes: 8