猿问

带有泛型的 Vavr 给出了不兼容的类型

任何人都可以解释为什么这个代码:


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语句,编译失败?不知道这里会发生什么。如何改进它?


人到中年有点甜
浏览 234回答 3
3回答

开满天机

您可以Try.of()使用返回的显式泛型类型进行调用以满足编译器检查。就像是:Try.<Seq<? extends Number>of(() -> List.of(1,2,3))Try.of()返回类型Try<T>其中T是供应商返回的类型。并且因为List.of(T t...)return 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 {&nbsp; &nbsp; default Try<Seq<? extends Number>> lol() {&nbsp; &nbsp; &nbsp; &nbsp; return Try.of(List::empty);&nbsp; &nbsp; }}class LolImpl implements Lol {&nbsp; &nbsp; @Override&nbsp; &nbsp; public Try<Seq<? extends Number>> lol() {&nbsp; &nbsp; &nbsp; &nbsp; return Try&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; .<Seq<? extends Number>>of(() -> List.of(1, 2, 3))&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; .onFailure(t -> System.out.println(t.getMessage()));&nbsp; &nbsp; }&nbsp; &nbsp; public static void main(String[] args) {&nbsp; &nbsp; &nbsp; &nbsp; System.out.println(new LolImpl().lol());&nbsp; &nbsp; }}输出:Success(List(1, 2, 3))通用示例类型推断问题进一步调查表明,这很可能是一个通用的编译器问题。看看下面的普通 Java 示例:import java.util.Arrays;import java.util.List;import java.util.function.Supplier;interface Some<T> {&nbsp; &nbsp; static <T> Some<T> of(Supplier<T> supplier) {&nbsp; &nbsp; &nbsp; &nbsp; return new SomeImpl<>(supplier.get());&nbsp; &nbsp; }&nbsp; &nbsp; default Some<T> shout() {&nbsp; &nbsp; &nbsp; &nbsp; System.out.println(this);&nbsp; &nbsp; &nbsp; &nbsp; return this;&nbsp; &nbsp; }&nbsp; &nbsp; class SomeImpl<T> implements Some<T> {&nbsp; &nbsp; &nbsp; &nbsp; private final T value;&nbsp; &nbsp; &nbsp; &nbsp; public SomeImpl(T value) {&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; this.value = value;&nbsp; &nbsp; &nbsp; &nbsp; }&nbsp; &nbsp; }&nbsp; &nbsp; static void main(String[] args) {&nbsp; &nbsp; &nbsp; &nbsp; final Some<List<CharSequence>> strings = Some.of(() -> Arrays.asList("a", "b", "c"));&nbsp; &nbsp; }}此代码编译没有任何问题,编译器Arrays.asList()从左侧的预期类型推断返回的类型:现在,如果我调用这个Some<T>.shout()方法,它什么都不做并返回Some<T>,编译器不是从预期的变量类型,而是从最后返回的类型推断类型:当然Arrays.asList("a","b","c")returnsList<String>和this is the typeshout()`方法推断并返回:指定显式类型Some<T>.of()可以解决问题,如Try.of()示例所示:我正在搜索有关类型推断的 Oracle 文档,并且有以下解释:Java 编译器利用目标类型来推断泛型方法调用的类型参数。表达式的目标类型是 Java 编译器期望的数据类型,具体取决于表达式出现的位置。来源:https&nbsp;:&nbsp;//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<List<Integer>>不是 的子类型Try<Seq<? extends Number>>。将lol()方法的返回类型更改为Try<? extends Seq<? extends Number>>,所有都将编译正常。让我们来详细看看。public Try<Seq<? extends Number>> lol() {&nbsp; // line 1&nbsp; &nbsp; return Try.of(() -> List.of(1, 2, 3))&nbsp; // line 2&nbsp; &nbsp; &nbsp; &nbsp; //.onFailure(Object::hashCode)&nbsp; &nbsp; &nbsp;// line 3&nbsp; &nbsp; ;}该lol()方法确实返回一个类型的值Try<Seq<? extends Number>>(参见第 1 行)。第 2 行中的 return 语句返回一个Try使用工厂方法构造的实例Try.of(...)。在 Vavr 0.9.x 中,它是这样定义的:static <T> Try<T> of(CheckedFunction0<? extends T> supplier) {&nbsp; &nbsp; // 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 部分)。如果我们现在取消注释.onFailure部分(第 3 行),那么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&nbsp; &nbsp; return Try.of(() -> List.of(1, 2, 3))&nbsp; &nbsp; // line 2&nbsp; &nbsp; &nbsp; &nbsp; .onFailure(Object::hashCode);&nbsp; &nbsp; &nbsp; &nbsp; // line 3}顺便说一句,在 Vavr 的整个类型层次结构中定义正确的泛型变量,尤其是对于集合,是创建 Vavr 时的难点之一。Java 的类型系统并不完善,还有一些东西是我们不能用 Java 的泛型来表达的。另请参阅我的博客文章“未来 Java 中的声明站点差异”免责声明:我是 Vavr(以前称为 Javaslang)的创建者

心有法竹

似乎 Java 编译器无法为您推断出正确的类型,在这种情况下,您需要提供继续操作所需的其他类型信息,例如:class LolImpl implements Lol {&nbsp; &nbsp; @Override&nbsp; &nbsp; public Try<Seq<? extends Number>> lol() {&nbsp; &nbsp; &nbsp; &nbsp; Try<Seq<? extends Number>> res = Try.of(() -> List.of(1, 2, 3));&nbsp; &nbsp; &nbsp; &nbsp; return res.onFailure(Object::hashCode);&nbsp; &nbsp; }}
随时随地看视频慕课网APP

相关分类

Java
我要回答