我怎样才能实现一个接受 Consumer<Optional<T>> 的方法,它在 T 中是逆变的?

How can I implement a method that accepts a Consumer<Optional<T>> that is contravariant in T?

在下面的示例中,我可以将 Consumer<Optional<Integer> 传递给 foo,但不能传递 Consumer<Optional<Number>>。另一方面,我可以将任何一种类型传递给 foo2,但是我无法从方法体中调用消费者的接受方法。有没有办法更改 foo 方法使其有效?我最初的直觉是尝试 void foo(Consumer<Result<? super T>> 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
    }
}

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

因此,编译器不会让您在需要 Optional<Number> 的地方传递 Optional<Integer>:它会阻止您调用神秘的 set 方法,以防万一你传入了 Double 而不是 Integer.

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

  • 转换——你知道这是安全的,因为 Optional 的不变性以及它缺乏消费者方法;但是你得到一个未经检查的警告(实际上可以抑制,因为 Optional 的属性)。
  • 创建正确类型的新 Optional - 可能更纯粹,但有创建新实例的运行时开销。

为了在方法中写这样的东西,你必须把它写成静态方法(可能在 test class 中,但也可能在其他地方); 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