使用JDK 1.8

时间:2016-04-04 21:34:41

标签: 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));
  }
}

通过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时仍然可以正常工作,对吗?

我做错了什么?

1 个答案:

答案 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

歧义错误有点令人困惑,但是,忽略返回类型,两种方法适用。