代码之家  ›  专栏  ›  技术社区  ›  Opal

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

  •  6
  • Opal  · 技术社区  · 5 年前

    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 声明?不知道这里发生了什么。如何改进?

    3 回复  |  直到 5 年前
        1
  •  5
  •   Szymon Stepniak    5 年前

    你可以打电话 Try.of() 返回显式泛型类型以满足编译器检查。比如:

    Try.<Seq<? extends Number>of(() -> List.of(1,2,3))
    

    Try<T> 哪里 T 供应商返回的类型。因为 List.of(T t...) List<T> ,则编译器看到的最终类型是 Try<List<Integer> 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()

    enter image description here

    现在,如果我称之为 Some<T>.shout() Some<T> ,编译器不是从预期的变量类型,而是从最后返回的类型推断类型:

    enter image description here

    当然 Arrays.asList("a","b","c") 退货 List<String> this is the type shout()`method推断并返回:

    enter image description here

    Some<T>.of() Try.of() 例子:

    enter image description here

    资料来源: https://docs.oracle.com/javase/tutorial/java/generics/genTypeInference.html#target_types

    “取决于表达式出现的位置” shout() 方法使编译器意识到 Some<List<CharSequence>> 当我们加上 方法开始返回 Some<List<String>> ,因为这就是 呼喊() 方法从返回的 Some.of()

        2
  •  5
  •   Daniel Dietrich    5 年前

    热释光;博士

    您的问题的答案与Java的类型推断以及类型方差(在我们的例子中是协方差)有关。这跟Vavr没有什么特别的关系。

    1. Try<List<Integer>> Try<? extends Seq<? extends Number>> .
    2. 尝试<列表<整数>&燃气轮机; 不是的子类型 Try<Seq<? extends Number>> .

    lol() 方法 尝试<?扩展序列<?扩展数字>&燃气轮机; 一切都会好起来的。


    让我们详细看看。

    public Try<Seq<? extends Number>> lol() {  // line 1
        return Try.of(() -> List.of(1, 2, 3))  // line 2
            //.onFailure(Object::hashCode)     // line 3
        ;
    }
    

    这个 lol() 方法不返回类型为的值 尝试<顺序<?扩展数字>&燃气轮机; (见第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 ? extends T ,即 ? extends Seq<? extends Number> ,与实际返回类型兼容 List<Integer>

    如果我们现在取消注释 .onFailure 部分(第3行),然后是泛型类型参数 T 尝试 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的泛型来表达。另请参阅我的博客文章 "Declaration-Site Variance in a Future Java"

        3
  •  2
  •   Oleksandr Pyrohov Andreas    5 年前

    似乎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);
        }
    }
    
    推荐文章