使用JDK 1.8时使用泛型类型的歧义参考

Joe*_*eau 9 java generics

我知道之前已经问过这个问题的变体,我认为我理解了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));
  }
}
Run Code Online (Sandbox Code Playgroud)

通过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
Run Code Online (Sandbox Code Playgroud)

Error:(18, 15) java: incompatible types: java.util.Collection<capture#1 of ? extends E> cannot be converted to java.util.List<E>
Run Code Online (Sandbox Code Playgroud)

现在除了这里的返回类型可用于解决歧义并避免两个错误这一事实,即使没有编译器这样做,为什么调用本身是模糊的?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));
}
Run Code Online (Sandbox Code Playgroud)

以上编译很好,即使这种类型应该被推断.

第一个例子在JDK 1.7下编译得很好,但是如果我使用JDK 1.8,即使我指定-source 1.7它,它仍然会导致错误.即使它在Java 8下是模糊的,编译器在设置为语言级别7时仍然可以正常工作,对吗?

我究竟做错了什么?

Rad*_*def 1

我从评论中提出了这一点,因为我想更好地解释它。

我很确定这有点像这个非通用示例:

static void m(Double a, Object b) {}
static void m(Object a, String b) {}

static {
    m(1.0, ""); // compiler error
}
Run Code Online (Sandbox Code Playgroud)

也就是说,由于某种原因,Function<? super T, E>被发现比 更具体。Function<? super T, ? extends E>

解释它的一种方法是Function<? super T, E>可以将其视为 的子类型Function<? super T, ? extends E>。这通常是正确的,例如List<String>is 的子类型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

歧义错误有点令人困惑,但是,无论返回类型如何,这两种方法适用。