【问题标题】:How can I implement a method that accepts a Consumer<Optional<T>> that is contravariant in T?如何实现一个接受 T 中逆变的 Consumer<Optional<T>> 的方法?
【发布时间】:2017-12-04 03:02:07
【问题描述】:

在以下示例中,我可以将Consumer&lt;Optional&lt;Integer&gt; 传递给foo,但不能传递Consumer&lt;Optional&lt;Number&gt;&gt;。另一方面,我可以将任一类型传递给foo2,但是我不能从方法体中调用消费者的accept 方法。有没有办法改变foo 方法以使其有效?我最初的直觉是尝试void foo(Consumer&lt;Result&lt;? super T&gt;&gt; c),但这显然并不意味着我会假设。

import java.util.Optional;
import java.util.function.Consumer;

public class test<T> {

    public void foo(Consumer<Optional<T>> c) {
        Optional<T> t = null;
        c.accept(t); // compiles
    }

    public void foo2(Consumer<? extends Optional<? super T>> c) {
        Optional<T> t = null;
        c.accept(t); // doesn't compile
    }

    public static void bar() {
        test<Integer> t = null;
        Consumer<Optional<Number>> crn = null;
        Consumer<Optional<Integer>> cri = null;

        t.foo(cri); // compiles
        t.foo(crn); // doesn't compile

        t.foo2(cri); // compiles
        t.foo2(crn); // compiles
    }
}

【问题讨论】:

    标签: java generics generic-variance


    【解决方案1】:

    原因是 Optional 从类型系统的角度来看并不特殊:我们知道 Optional 只有一个提供者方法 (Optional.get()) 并且它没有消费者方法(如Optional.set(T));但编译器没有。

    所以,编译器不会让你在需要 Optional&lt;Number&gt; 的地方传递 Optional&lt;Integer&gt;:它会阻止你调用那个神秘的 set 方法,以防你传入 Double Integer.

    解决此问题的唯一方法是将Optional&lt;T&gt; 更改为Optional&lt;S&gt;,其中ST 的超类型。您可以通过以下任一方式执行此操作:

    • 铸造 - 你知道这是安全的,因为 Optional 的不变性和它缺乏消费者方法;但是您会收到未经检查的警告(实际上可以抑制,因为 Optional 的属性)。
    • 创建正确类型的新 Optional - 可能更纯粹,但会产生创建新实例的运行时开销。

    为了在方法中编写这样的东西,您必须将其编写为静态方法(可能在test 类中,但也可能在其他地方); Java 的类型系统表达能力不够强,无法在实例方法的签名上编写所需的约束:

    public static <T, S extends T> void foo3(Consumer<Optional<T>> c, test<S> test) {
        Optional<S> s = null;
    
        @SuppressWarnings("unchecked")  // Safe because of properties of Optional.
        Optional<T> t = (Optional<T>) (Optional<?>) s;
    
        c.accept(t);
    }
    

    并像这样调用(使用问题代码中的cricrnt 的值):

    foo3(cri, t); // compiles
    foo3(crn, t); // compiles
    

    Ideone demo

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2011-03-18
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2011-02-14
      • 2015-01-01
      • 1970-01-01
      相关资源
      最近更新 更多