【问题标题】:Why can't the Java compiler figure this out?为什么 Java 编译器不能解决这个问题?
【发布时间】:2008-12-22 22:47:29
【问题描述】:

为什么编译器无法在以下示例中从 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());
            }
        });
    }

Collections 上的类型安全方法的全部意图是避免单例集合出现此类问题(而不是使用静态变量)。那么编译器是否根本无法跨多个泛型级别执行推理?怎么回事?

【问题讨论】:

    标签: java generics collections


    【解决方案1】:

    Java 的推理需要大量的帮助。在很多情况下,类型系统可以更好地推断,但在您的情况下,以下将起作用:

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

    【讨论】:

    • Java 可以做得更好并且正在考虑用于 Java 7 的示例类似于: Map model = new HashMap();
    • 太好了,我以前没见过这种语法。谢谢!
    • 就用Bloch在Effective Java第2版中介绍的GenericFactory吧……
    【解决方案2】:

    首先你可以把你的问题缩小到这段代码:

    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?

    【讨论】:

    • 好文章。非常具体地说,回答原始问题的关键点似乎是:“类型推断不考虑方法调用。”
    【解决方案3】:

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

    解决方案是明确声明您需要 Collections.&lt;String&gt;emptySet(),如 GaryF 所述。

    【讨论】:

      【解决方案4】:

      这看起来像是一个类型转换问题 - 即,需要将 Object(在 Set&lt;Object&gt; 中,这将是空集的类型)转换为 String。在一般情况下,向下转型并不安全。

      【讨论】:

        猜你喜欢
        • 2018-12-02
        • 2013-02-03
        • 2022-07-27
        • 1970-01-01
        • 2021-07-28
        • 1970-01-01
        • 2013-04-21
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多