我知道之前已经问过这个问题的变体,并且我认为我理解了Java 8类型解析系统,但我得到了一个模糊的引用错误,我真的认为它不应该含糊不清:
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.List;
interface Function <T, E> {}
public class MyFns {
public static <E, T> Collection<E> map(Function<? super T, E> fn, Collection<T> coll) {
return new ArrayList<>();
}
public static <E, T> List<E> map(Function<? super T, ? extends E> fn, List<T> coll) {
return new ArrayList<>();
}
public static <E, T> List<E> map(Function<? super T, ? extends E> fn, T[] coll) {
return map(fn, Arrays.asList(coll));
}
}
通过JDK 1.8编译时,第三个重载会产生两个编译错误:
Error:(18, 12) java: reference to map is ambiguous
both method <E,T>map(Function<? super T,E>,java.util.Collection<T>) in MyFns and method <E,T>map(Function<? super T,? extends E>,java.util.List<T>) in MyFns match
和
Error:(18, 15) java: incompatible types: java.util.Collection<capture#1 of ? extends E> cannot be converted to java.util.List<E>
现在除了这里的返回类型可用于解决歧义并避免错误之外,即使没有编译器这样做,为什么调用本身不明确? Function<? super T, ? extends E>
不应该转换为Function <? super T, E>
,因为后者具有更具体的类型,不包含通配符。其次,即使它是,编译器也不应该选择更具体的重载,在这种情况下,将第二个参数作为List<T>
而不是Collection<T>
?我在这里缺少什么?
如果我在第三次重载中调用map中的类型时,这确实有效:
public static <E, T> List<E> map(Function<? super T, ? extends E> fn, T[] coll) {
return MyFns.<E,T>map(fn, Arrays.asList(coll));
}
以上编辑很好,尽管无论如何都应该推断出这种类型。
第一个例子在JDK 1.7下编译得很好,但是如果我使用JDK 1.8,即使我指定-source 1.7
,它仍然会导致错误。即使它在Java 8下是不明确的,编译器在设置为语言级别7时仍然可以正常工作,对吗?
我做错了什么?
答案 0 :(得分:0)
我从评论中解除了这个问题,因为我想更好地解释一下。
我非常确定这有点像这个非通用的例子:
static void m(Double a, Object b) {}
static void m(Object a, String b) {}
static {
m(1.0, ""); // compiler error
}
也就是说,Function<? super T, E>
由于某种原因被发现比Function<? super T, ? extends E>
更具体。
解释它的一种方法是Function<? super T, E>
可以被视为Function<? super T, ? extends E>
的子类型。这通常是正确的,例如List<String>
是List<? extends String>
的子类型,但我对类型推断不够熟悉,以确保这肯定是示例的工作原理。
直观地解释它的另一种方法是非通配符E
总是与其参数完全匹配,因此它总是更具体。
这些对我来说都不够好,特别是因为你说这是在1.7下为你编译的,然后存在使用通配符的子类型。这实际上归结为类型推断的变化。
我认为真正的答案是由18.5.4指定的,我现在还不太清楚,不能肯定地说。 (通常也太密集了,不能引用...)
Function<? super T, ? extends E>
不应该投放到Function <? super T, E>
,因为后者有一个更具体的类型,不包含通配符。
这是对作业的正确直觉,但是当您将fn
传递给通用方法时,会捕获通配符。参见例如https://docs.oracle.com/javase/tutorial/java/generics/capture.html
歧义错误有点令人困惑,但是,忽略返回类型,两种方法都适用。