[英]Vavr with generics gives incompatible types
任何人都可以解释为什么这个代码:
interface Lol {
default Try<Seq<? extends Number>> lol() {
return Try.of(List::empty);
}
}
class LolImpl implements Lol {
@Override
public Try<Seq<? extends Number>> lol() {
return Try
.of(() -> List.of(1, 2, 3))
//.onFailure(Object::hashCode)
;
}
}
如果我取消注释onFailure
语句,则无法编译? 不知道这里发生了什么。 怎么改进呢?
您可以调用Try.of()
并返回显式泛型类型以满足编译器检查。 就像是:
Try.<Seq<? extends Number>of(() -> List.of(1,2,3))
Try.of()
返回类型Try<T>
,其中T
是供应商返回的类型。 并且因为List.of(T t...)
返回List<T>
,所以编译器看到的最终类型是Try<List<Integer>
,这不是返回类型定义的方法。 具有特定类型的Java泛型是不变的,它们不支持协变或逆变换,因此List<Integer> != List<Number>
。
工作范例:
import io.vavr.collection.List;
import io.vavr.collection.Seq;
import io.vavr.control.Try;
interface Lol {
default Try<Seq<? extends Number>> lol() {
return Try.of(List::empty);
}
}
class LolImpl implements Lol {
@Override
public Try<Seq<? extends Number>> lol() {
return Try
.<Seq<? extends Number>>of(() -> List.of(1, 2, 3))
.onFailure(t -> System.out.println(t.getMessage()));
}
public static void main(String[] args) {
System.out.println(new LolImpl().lol());
}
}
输出:
Success(List(1, 2, 3))
进一步的调查表明,这很可能是一个通用的编译器问题。 看看下面的普通Java示例:
import java.util.Arrays;
import java.util.List;
import java.util.function.Supplier;
interface Some<T> {
static <T> Some<T> of(Supplier<T> supplier) {
return new SomeImpl<>(supplier.get());
}
default Some<T> shout() {
System.out.println(this);
return this;
}
class SomeImpl<T> implements Some<T> {
private final T value;
public SomeImpl(T value) {
this.value = value;
}
}
static void main(String[] args) {
final Some<List<CharSequence>> strings = Some.of(() -> Arrays.asList("a", "b", "c"));
}
}
此代码编译没有任何问题,编译器推断Arrays.asList()
从左侧的预期类型返回的类型:
现在,如果我调用Some<T>.shout()
方法,它什么都不做并返回Some<T>
,编译器推断的类型不是来自预期的变量类型,而是来自最后返回的类型:
当然Arrays.asList("a","b","c")
返回List<String>
, this is the type
shout()`方法推断并返回:
指定Some<T>.of()
Try.of()
显式类型可以解决问题,如Try.of()
示例中所示:
我正在搜索关于类型推断的Oracle文档,并且有这样的解释:
Java编译器利用目标类型来推断泛型方法调用的类型参数。 表达式的目标类型是Java编译器所期望的数据类型,具体取决于表达式的显示位置。
资料来源: https : //docs.oracle.com/javase/tutorial/java/generics/genTypeInference.html#target_types
看起来这“取决于表达式出现的位置”在这种情况下意味着来自先前返回的确切类型的推断类型。 这将解释为什么跳过shout()
方法使编译器意识到,我们期望Some<List<CharSequence>>
并且当我们添加shout()
方法时它开始返回Some<List<String>>
,因为这就是shout()
方法从返回的Some.of()
方法类型中看到。 希望能帮助到你。
您的问题的答案与Java的类型推断以及类型方差(在我们的案例中为协方差)有关。 它与Vavr无关。
Try<List<Integer>>
是Try<? extends Seq<? extends Number>>
的子类型 Try<? extends Seq<? extends Number>>
Try<? extends Seq<? extends Number>>
。 Try<List<Integer>>
不是Try<Seq<? extends Number>>
的子类型Try<Seq<? extends Number>>
Try<Seq<? extends Number>>
。 将lol()
方法的返回类型更改为Try<? extends Seq<? extends Number>>
Try<? extends Seq<? extends Number>>
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>>
类型的值Try<Seq<? extends Number>>
Try<Seq<? extends Number>>
(见第1行)。
第2行中的return语句返回使用工厂方法Try.of(...)
构造的Try
实例。 在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()
的返回类型和工厂方法Try.of
的CheckedFunction0
签名。
编译很好,因为supplier
函数返回的类型值? extends T
? extends T
,这是? extends Seq<? extends Number>
? extends Seq<? extends Number>
? extends Seq<? extends Number>
,它与实际返回类型List<Integer>
兼容(参见上面的TL; DR部分)。
如果我们现在取消注释.onFailure
部分(第3行),那么工厂方法Try.of
的泛型类型参数T
不再具有返回类型lol()
的范围。 编译器推断T
为List<Integer>
因为它总是试图找到适用的最具体的类型。
.onFailure
返回List<Integer>
类型的值,因为如果它的实例返回完全相同的类型。 但是Try<List<Integer>>
不是Try<Seq<? extends Number>>
的子类型Try<Seq<? extends Number>>
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)的创始人
看起来像Java编译器无法为您推断出正确的类型,在这种情况下,您需要提供继续执行所需的其他类型信息,例如:
class LolImpl implements Lol {
@Override
public Try<Seq<? extends Number>> lol() {
Try<Seq<? extends Number>> res = Try.of(() -> List.of(1, 2, 3));
return res.onFailure(Object::hashCode);
}
}
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.