为什么Java编译器无法理解这个?

3

为什么编译器在以下示例中无法推断出从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()的以下实现是有效的:

现在,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());
            }
        });
    }

Collections中的类型安全方法的整个意图是避免使用单例集合时出现此类问题(而不是使用静态变量)。那么,编译器是否无法跨多层泛型执行推理?发生了什么事情?

4个回答

8

Java的类型推断需要很多辅助。在许多情况下,类型系统可以更好地进行推断,但在您的情况下,以下内容将起作用:

print("Collections.<String>emptySet();");

Java 可以做得更好的一个例子,也被考虑用于 Java 7 的是这样的:Map<String,Integer> model = new HashMap<>(); - GaryF
太好了,我以前没见过这个语法。谢谢! - Kris Nuttycombe
1
只需使用Bloch在《Effective Java》第二版中介绍的GenericFactory即可。 - André

5

首先,您可以将问题缩小到以下代码:

public class Test { 
    public void option(Collection<String> b) {
    }

    public void getColl() {
        option(Collections.emptySet());
    }
}

这样是不行的,你需要一个临时变量,否则编译器无法推断类型。这里有一个很好的解释: 为什么在调用泛型方法时需要临时变量?


不错的文章。非常明确,回答原问题的关键点似乎是:“方法调用不考虑类型推断。” - Dave Costa

1

Collections.emptySet() 不是一个 Collection<String>,除非 Java 知道它需要一个 Collection<String>。在这种情况下,编译器似乎对尝试确定类型的顺序有些愚蠢,并且在尝试确定 B 的预期模板参数类型实际上是 String 之前,尝试确定 Collections.emptySet() 的返回类型。

解决方案是明确声明您需要 Collections.<String>emptySet(),如 GaryF 所提到的。


0

看起来像是类型转换问题 - 即,它被要求将 Object(在 Set<Object> 中,这将是空集的类型)强制转换为 String。在一般情况下,向下转换是不安全的。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接