除非内联 lambda 表达式,否则 Javac 无法推断类型

Javac can't infer type unless lambda expression is inlined

我有以下使用 Vavr 的 Java 代码片段。除非我内联一个参数,否则类型检查会失败。

为什么下面的代码不能被编译器接受?

import io.vavr.Function1;
import io.vavr.Tuple;
import io.vavr.Tuple2;
import io.vavr.collection.List;
import io.vavr.Option;

import static io.vavr.collection.List.unfoldRight;

class A {}
class B {}
class Main {
    Function1<A, Option<Tuple2<B, A>>> f = (a) -> Option.of(Tuple.of(new B(), new A()));
    List<B> L0 = unfoldRight(new A(), f); // *
    List<B> L1 = unfoldRight(new A(), (a) -> Option.of(Tuple.of(new B(), new A()));

    Option<Tuple2<B, A>> g(A a) { return Option.of(Tuple.of(new B(), new A())); }
    List<B> L2 = unfoldRight(new A(), (a) -> g(a)); // **
}


// *  Compilation fails with: "Incompatible equality constraint: ? extends T and A"

// ** Compilation fails with: "Incompatible equality constraint: ? extends A and A"

这是 Vavr 库中 unfoldRight 的方法签名:

static <T, U> List<U> unfoldRight(T seed, Function<? super T, Option<Tuple2<? extends U, ? extends T>>> f)

这里是 link 的 Github 文档:

https://github.com/vavr-io/vavr/blob/master/vavr/src/main/java/io/vavr/collection/List.java#L644-L671

关键是 Option<Tuple<A, B>> 不是 Option<Tuple<? extends A, ? extends B>> 的实例(尽管它是 Option<? extends Tuple<? extends A, ? extends B>>)。

考虑 List<Map<A, B>>List<Map<? extends A, ? extends B>> 的情况(从类型安全的角度来看,这与您的代码相同)。如果你能写:

List<Map<A, B>> list = new ArrayList<>();

// Compiler error! Pretend it's OK, though.
List<Map<? extends A, ? extends B>> list2 = list;

Map<SubclassOfA, SubclassOfB> map = new HashMap<>();
list2.add(map);

list.get(0).put(new A(), new B());

现在这是个问题,因为 map 包含 key/value 对类型 A,B,而不是 SubclassOfA,SubclassOfB。因此,如果您尝试从 map.

获取内容,您将得到 ClassCastException
// ClassCastException!
SubclassOfA soa = map.keySet().iterator().next();

Ideone demo

因此,编译器不允许它。

如果list2被声明为List<? extends Map<? extends A, ? extends B>>,你不能调用list2.add(map),所以你不能得到同样的问题。因此,该分配将被允许。

为您的类型添加通配符上限:

Function1<A, Option<Tuple2<? extends B, ? extends A>>> f = ...
Option<Tuple2<? extends B, ? extends A>> g(A a) { ... }