为什么编译器无法在以下示例中推断出Collections.emptySet()的结果的正确类型?
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());
}
});
}
}
这是编译器错误消息:
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
现在,getColl()的以下实现当然有效:
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());
}
});
}
并且关于集合的类型安全方法的整个意图是避免使用单例集合的这种问题(而不是使用静态变量。)那么编译器是否无法跨多个泛型级别执行推理?发生了什么事?
答案 0 :(得分:8)
Java需要通过推理进行大量的实践。类型系统可以在很多情况下更好地推断,但在您的情况下,以下方法将起作用:
print("Collections.<String>emptySet();");
答案 1 :(得分:5)
首先,您可以将问题范围缩小到以下代码:
public class Test {
public void option(Collection<String> b) {
}
public void getColl() {
option(Collections.emptySet());
}
}
这不起作用,需要一个临时变量,否则编译器无法推断出类型。以下是对此问题的一个很好的解释:Why do temporary variables matter in case of invocation of generic methods?
答案 2 :(得分:1)
Collections.emptySet()
不是Collection<String>
,除非Java知道它需要Collection<String>
。在这种情况下,似乎编译器对它尝试确定类型的顺序有些愚蠢,并且在尝试确定B的实际模板参数类型之前尝试确定Collections.emptySet()
的返回类型{ {1}}。
解决方案是明确说明你需要String
,如GaryF所述。
答案 3 :(得分:0)
它看起来像是一个类型转换问题 - 也就是说,需要将Object
(在Set<Object>
中,这将是空集的类型)转换为String
。在一般情况下,堕落者不是安全的。