11

在Java 8 lambdas、Function.identity() 或 t->t中找到的答案似乎暗示这Function.identity()几乎总是等同于t -> t. 但是,在下面看到的测试用例中,替换t -> tFunction.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对此案有什么看法?

4

2 回答 2

5

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

这不是 javac/ecj 第一次在类型参数的推断中表现不同。

在这种情况下,您可以使用 Function.<Integer>identity() 给 javac 一个提示,使其可以使用 javac 进行编译。

对于 Function.identity() 和 t->t 的区别:

  • Function.identity() 是 Function<T,T>
  • t->t 在这种情况下是 Function<? 超整数,? 扩展整数>

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

于 2016-06-26T05:35:28.770 回答
1

The difference between i -> i and Function.identity() is apparent in situation where (1) collectors are nested and (2) upcast is needed somewhere in deep level of nesting.

Example: Suppose we are to classify elements in List<Object> into map with particular lists by element class. (It resembles Guava ClassToInstanceMap except the value is List, so something like hypothetical ClassToInstanceMultimap.) The downstream collector toList() normally collects values into List<Object>. However, if the value type of map is wildcard type List<?>, type inference cannot simply match it. The solution is to adapt collector to pretend it collects List<?>, which is done by intermediate collectingAndThen collector.

The point is now, what should the finisher function look like? Lambda i -> i behaves like Function<List<Object>, List<?>>, which allows upcast. Function.identity() with fixed input and output type T gives no room for upcast we need, hence code won't compile.

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);
}
于 2021-03-15T13:08:51.927 回答