为什么Function.identity()会破坏类型设置但是t - >不是吗?

时间:2016-06-26 03:34:21

标签: java generics reification

Java 8 lambdas, Function.identity() or t->t找到的答案似乎暗示Function.identity()几乎总是等同于t -> t。但是,在下面看到的测试用例中,将t -> t替换为Function.identity()会导致编译器错误。那是为什么?

public class Testcase {

    public static <T, A, R, K, V> Collector<T, A, R> comparatorOrdering(
            Function<? super T, ? extends K> keyMapper,
            Function<? super T, ? extends V> valueMapper,
            Comparator<? super K> keyComparator,
            Comparator<? super V> valueComparator) {
        return null;
    }

    public static void main(String[] args) {    
        Map<Integer, String> case1 = Stream.of(1, 2, 3).
                collect(comparatorOrdering(t -> t, t -> String.valueOf(t),
                        Comparator.naturalOrder(), Comparator.naturalOrder()));
        Map<Integer, String> case2 = Stream.of(1, 2, 3).
                collect(comparatorOrdering(Function.identity(), t -> String.valueOf(t),
                        Comparator.naturalOrder(), Comparator.naturalOrder()));
    }
}

案例1编译得很好,但案例2失败了:

method comparatorOrdering in class Testcase cannot be applied to given types;
                collect(comparatorOrdering(Function.identity(), t -> String.valueOf(t),
  required: Function<? super T#1,? extends K>,Function<? super T#1,? extends V>,Comparator<? super K>,Comparator<? super V>
  found: Function<Object,Object>,(t)->Strin[...]Of(t),Comparator<T#2>,Comparator<T#3>
  reason: inferred type does not conform to upper bound(s)
    inferred: Object
    upper bound(s): Comparable<? super T#4>,T#4,Object
  where T#1,A,R,K,V,T#2,T#3,T#4 are type-variables:
    T#1 extends Object declared in method <T#1,A,R,K,V>comparatorOrdering(Function<? super T#1,? extends K>,Function<? super T#1,? extends V>,Comparator<? super K>,Comparator<? super V>)
    A extends Object declared in method <T#1,A,R,K,V>comparatorOrdering(Function<? super T#1,? extends K>,Function<? super T#1,? extends V>,Comparator<? super K>,Comparator<? super V>)
    R extends Object declared in method <T#1,A,R,K,V>comparatorOrdering(Function<? super T#1,? extends K>,Function<? super T#1,? extends V>,Comparator<? super K>,Comparator<? super V>)
    K extends Object declared in method <T#1,A,R,K,V>comparatorOrdering(Function<? super T#1,? extends K>,Function<? super T#1,? extends V>,Comparator<? super K>,Comparator<? super V>)
    V extends Object declared in method <T#1,A,R,K,V>comparatorOrdering(Function<? super T#1,? extends K>,Function<? super T#1,? extends V>,Comparator<? super K>,Comparator<? super V>)
    T#2 extends Comparable<? super T#2>
    T#3 extends Comparable<? super T#3>
    T#4 extends Comparable<? super T#4> declared in method <T#4>naturalOrder()

我的环境是Windows 10,64位,Oracle JDK build 1.8.0_92-b14。

更新:看到这个在ecj下编译,我有一个后续问题:这是javac中的错误吗? JLS对此案有什么看法?

2 个答案:

答案 0 :(得分:5)

Ecj能够推断出正确的(?)类型参数(整数)以匹配约束。 Javac出于某种原因得出了不同的结果。

这不是javac / ecj第一次在推理类型参数方面表现不同。

在这种情况下,你可以给javac一个提示函数。&lt; Integer&gt; identity()以使它可以用javac编译。

对于Function.identity()和t-&gt; t:

之间的区别
  • Function.identity()是函数&lt; T,T&gt;
  • 在这种情况下,
  • t-> t是功能&lt;?超级整数,?扩展整数&gt;

所以t-> t在它可以匹配的方法中更灵活。

答案 1 :(得分:0)

i -> iFunction.identity() 之间的区别在以下情况下很明显:(1) 收集器嵌套和 (2) upcast 需要在深层嵌套的某处。< /p>

示例:假设我们要将 List<Object> 中的元素按元素类别分类到具有特定列表的映射中。 (它类似于 Guava ClassToInstanceMap,但值是 List,所以类似于假设的 ClassToInstanceMultimap。)下游收集器 toList() 通常将值收集到 List<Object> 中。但是,如果map的值类型是通配符类型List<?>,类型推断不能简单地匹配它。解决方案是调整收集器以假装它收集 List<?>,这是由中间 collectingAndThen 收集器完成的。

现在的重点是,finisher 函数应该是什么样的? Lambda i -> i 的行为类似于 Function<List<Object>, List<?>>,允许向上转换。具有固定输入和输出类型 Function.identity()T 没有我们需要的向上转换的空间,因此代码将无法编译。

import static java.util.function.Function.identity;
import static java.util.stream.Collectors.collectingAndThen;
import static java.util.stream.Collectors.groupingBy;
import static java.util.stream.Collectors.toList;

public static void main(String[] args) {
    class Apple {}
    class Pear {}
    List<Object> list = List.of(new Apple(), new Pear(), new Apple());
    Map<Class<?>, List<Object>> obj;  
    obj = list.stream().collect(groupingBy(Object::getClass, toList())); // compiles
    Map<Class<?>, List<?>> wild;
    wild = list.stream().collect(groupingBy(Object::getClass, toList())); // wont compile
    wild = list.stream().collect(groupingBy(Object::getClass, collectingAndThen(toList(), i -> i)));  // compiles
    wild = list.stream().collect(groupingBy(Object::getClass, collectingAndThen(toList(), identity())));  // wont compile
    wild = list.stream().collect(groupingBy(Object::getClass, collectingAndThen(toList(), (List<Object> i) -> (List<?>)i)));  // compiles
    System.out.println(wild);
}