【问题标题】:Type inference weirdness in Eclipse NeonEclipse Neon 中的类型推断怪异
【发布时间】:2017-07-06 06:25:44
【问题描述】:

大家早上好,

我一直在努力理解为什么以下代码不能在 Eclipse Neon (JDT 3.12.3.v20170301-0400) 中编译,但可以使用 javac 或 Eclipse Mars 完美编译:

public class TestLambda {

    protected static <K, V> Map<K, V> newMap(final Function<K, V> loader) {
        return new HashMap<>();
    }

    private final Map<Integer, Integer> working = newMap(key -> {

        final List<String> strings = new ArrayList<>();

        final String[] array = strings.toArray(new String[strings.size()]);
        foo(array);

        return null;
    });

    private final Map<Void, Void> notWorking = newMap(key -> {

        final List<String> strings = new ArrayList<>();

        // This line seems to be the root of all evils
        foo(strings.toArray(new String[strings.size()]));

        return null;
    });

    private void foo(final String[] x) {}

    private void foo(final Integer[] x) {}

}

Eclipse 编译器显示“类型不匹配:无法从 Map&lt;Object,Object&gt; 转换为 Map&lt;Void,Void&gt;”。

它似乎无法知道必须调用哪个 foo 方法...

我做错了吗?

提前感谢您的帮助

【问题讨论】:

  • 我很困惑:错误发生在哪一行?您突出显示的那个根本不使用地图。 foo(Integer[]) 是干什么用的?
  • 我知道,有问题的代码中没有使用 Map。只需在 Eclipse Neon 中加载这段代码,看看会发生什么......
  • 这段实际的代码并没有做任何有用的事情,甚至根本没有任何意义......它只是其他代码的精简版本,试图说明和隔离有问题的行为
  • 您是说foo(Integer []) 重载必须存在才能显示此行为?如果没有,你也应该把它去掉。 (我在手机自动取款机上)。
  • 是的,它必须在那里......

标签: java eclipse generics lambda eclipse-neon


【解决方案1】:

即使使用最新版本 Eclipse Oxygen,我也可以重现该问题。任何javac 版本的 Java 8 或 Java 9 测试版都不会出现此问题。

我们可以清楚地得出结论,这是一个错误和一个奇怪的错误。

首先,这里没有歧义。 foo 的两次调用都必须以 foo(String[]) 结尾,事实上,Eclipse 不会报告歧义问题。

其次,foo 调用对这个 lambda 表达式的函数类型没有影响。

如果我们将行 foo(strings.toArray(new String[strings.size()])); 更改为 foo(strings.toArray());,我们将在 foo 处得到一个真正的错误,因为不存在 foo(Object[]),但类型推断将正确解析 lambda 表达式的 Function&lt;Void, Void&gt; 和 @987654330 @ 作为newMap 的返回类型。

我们还可以通过将 foo 声明更改为来创建真正的歧义问题

private void foo(final Serializable[] x) {}
private void foo(final CharSequence[] x) {}

这将导致foo 调用都出错,因为String[] 与这两种方法都兼容,并且SerializableCharSequence 之间没有关系,但newMap 调用的类型推断不受影响并按预期解析为
&lt;Void, Void&gt; Map&lt;Void, Void&gt; newMap(Function&lt;Void, Void&gt;)

最奇怪的是,只需交换 foo 方法的顺序,错误就会消失:

public class TestLambda {

    protected static <K, V> Map<K, V> newMap(final Function<K, V> loader) {
        return new HashMap<>();
    }

    private final Map<Integer, Integer> working = newMap(key -> {

        final List<String> strings = new ArrayList<>();

        final String[] array = strings.toArray(new String[strings.size()]);
        foo(array);

        return null;
    });

    private final Map<Void, Void> notWorking = newMap(key -> {

        final List<String> strings = new ArrayList<>();

        // This line seems to be the root of all evils
        foo(strings.toArray(new String[strings.size()]));

        return null;
    });

    private void foo(final Integer[] x) {}
    private void foo(final String[] x) {}
}

编译正常。

它背后有一个模式。您可以拥有任意数量的foo 重载,只要最后声明的重载适用foo 调用。它不一定是最终被调用的最具体的,例如在课程末尾放置另一个private void foo(final Object[] x) {} 也可以解决问题。

那个模式肯定没有匹配的 Java 语言规则……

【讨论】:

  • 感谢您的研究和结论......在我的生产代码中,“foo”方法在另一个类中......我将看看改变它的位置是否能解决问题。我一定会提交一个错误。
  • 我更改了我的生产代码并移动了调用的方法,使其位于最后一个位置,并且它的编译就像一个魅力:-)
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2019-04-27
  • 1970-01-01
  • 2012-03-13
  • 2016-02-29
  • 1970-01-01
  • 2020-08-12
  • 2019-02-21
相关资源
最近更新 更多