TL;博士
您的问题的答案与 Java 的类型推断和类型变化(在我们的例子中是协变)有关。它与 Vavr 无关。
Try<List<Integer>>
是 的子类型Try<? extends Seq<? extends Number>>
。
- 但
Try<List<Integer>>
不是Try<Seq<? extends Number>>
.
lol()
将方法的返回类型更改为Try<? extends Seq<? extends Number>>
,一切都将正常编译。
让我们来详细看看。
public Try<Seq<? extends Number>> lol() { // line 1
return Try.of(() -> List.of(1, 2, 3)) // line 2
//.onFailure(Object::hashCode) // line 3
;
}
该lol()
方法确实返回一个类型的值Try<Seq<? extends Number>>
(见第 1 行)。
第 2 行中的 return 语句返回一个Try
使用工厂方法构造的实例Try.of(...)
。在 Vavr 0.9.x 中,它是这样定义的:
static <T> Try<T> of(CheckedFunction0<? extends T> supplier) {
// implementation omitted
}
编译器推断:
// type T = Seq<? extends Number>
Try.of(() -> List.of(1, 2, 3))
因为它需要同时匹配方法的返回类型和工厂方法lol()
的CheckedFunction0
签名Try.of
。
这编译得很好,因为supplier
函数返回一个 type 的值? extends T
,即? extends Seq<? extends Number>
,它与实际的返回类型兼容List<Integer>
(参见上面的 TL;DR 部分)。
如果我们现在取消注释该部分(第 3 行),则工厂方法.onFailure
的泛型类型参数不再具有返回类型的范围。编译器推断是因为它总是试图找到最具体的适用类型。T
Try.of
lol()
T
List<Integer>
.onFailure
返回该类型的值,List<Integer>
因为如果它的实例返回完全相同的类型。但Try<List<Integer>>
不是Try<Seq<? extends Number>>
(参见上面的 TL;DR 部分)的子类型,因此代码不再编译。
使lol()
方法在其返回类型中协变将使编译器满意:
// before: Try<Seq<? extends Number>>
Try<? extends Seq<? extends Number>> lol() { // line 1
return Try.of(() -> List.of(1, 2, 3)) // line 2
.onFailure(Object::hashCode); // line 3
}
顺便说一句,在 Vavr 的整个类型层次结构中定义正确的通用方差,特别是对于集合,是创建 Vavr 时的困难部分之一。Java 的类型系统并不完美,还有一些东西是我们无法用 Java 的泛型来表达的。另请参阅我的博客文章“未来 Java 中的声明站点差异”
免责声明:我是 Vavr(以前称为 Javaslang)的创建者