Eclipse Neon 中的类型推断异常
Type inference weirdness in Eclipse Neon
大家早上好,
一段时间以来,我一直在努力理解为什么以下代码不能在 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 编译器说 "Type mismatch: cannot convert from Map<Object,Object>
to Map<Void,Void>
"。
它似乎无法知道必须调用哪个 foo 方法...
我做错了什么吗?
在此先感谢您的帮助
即使使用最新版本 Eclipse Oxygen,我也可以重现该问题。 Java 8 或 Java 9 beta 的任何 javac
版本都不会出现此问题。
我们可以清楚地得出结论,这是一个错误,而且很奇怪。
首先,这里没有歧义。 foo
的两次调用都必须在 foo(String[])
结束,事实上,Eclipse 不会报告歧义问题。
其次,foo
调用对该 lambda 表达式的函数类型没有影响。
如果我们将行 foo(strings.toArray(new String[strings.size()]));
更改为 foo(strings.toArray());
,我们将在 foo
处得到一个真正的错误,因为 foo(Object[])
不存在,但类型推断将正确解析Function<Void, Void>
作为 lambda 表达式,Map<Void, Void>
作为 newMap
的 return 类型。
我们还可以通过将 foo
声明更改为
来创建真正的歧义问题
private void foo(final Serializable[] x) {}
private void foo(final CharSequence[] x) {}
这将导致两个 foo
调用都出错,因为 String[]
与这两种方法兼容并且 Serializable
和 CharSequence
之间没有关系,但是newMap
调用的类型推断不受影响并解析为
<Void, Void> Map<Void, Void> newMap(Function<Void, Void>)
符合预期。
最奇怪的是,只需交换 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
调用。它不一定是最终调用的最具体的一个,例如在 class 的末尾放置另一个 private void foo(final Object[] x) {}
也可以解决问题。
那个模式肯定没有匹配的 Java 语言规则…
大家早上好,
一段时间以来,我一直在努力理解为什么以下代码不能在 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 编译器说 "Type mismatch: cannot convert from Map<Object,Object>
to Map<Void,Void>
"。
它似乎无法知道必须调用哪个 foo 方法...
我做错了什么吗?
在此先感谢您的帮助
即使使用最新版本 Eclipse Oxygen,我也可以重现该问题。 Java 8 或 Java 9 beta 的任何 javac
版本都不会出现此问题。
我们可以清楚地得出结论,这是一个错误,而且很奇怪。
首先,这里没有歧义。 foo
的两次调用都必须在 foo(String[])
结束,事实上,Eclipse 不会报告歧义问题。
其次,foo
调用对该 lambda 表达式的函数类型没有影响。
如果我们将行 foo(strings.toArray(new String[strings.size()]));
更改为 foo(strings.toArray());
,我们将在 foo
处得到一个真正的错误,因为 foo(Object[])
不存在,但类型推断将正确解析Function<Void, Void>
作为 lambda 表达式,Map<Void, Void>
作为 newMap
的 return 类型。
我们还可以通过将 foo
声明更改为
private void foo(final Serializable[] x) {}
private void foo(final CharSequence[] x) {}
这将导致两个 foo
调用都出错,因为 String[]
与这两种方法兼容并且 Serializable
和 CharSequence
之间没有关系,但是newMap
调用的类型推断不受影响并解析为
<Void, Void> Map<Void, Void> newMap(Function<Void, Void>)
符合预期。
最奇怪的是,只需交换 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
调用。它不一定是最终调用的最具体的一个,例如在 class 的末尾放置另一个 private void foo(final Object[] x) {}
也可以解决问题。
那个模式肯定没有匹配的 Java 语言规则…