0

在下面的示例中,我可以将 a 传递Consumer<Optional<Integer>foo,但不能传递 a 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
    }
}
4

1 回答 1

1

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

所以,编译器不会让你在需要 an 的Optional<Integer>地方传递一个Optional<Number>:它会阻止你调用那个神秘的set方法,以防你传递的是 aDouble而不是 an Integer

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

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

为了在方法中编写这样的东西,您必须将其编写为静态方法(可能在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);
}

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

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

Ideone demo

于 2017-06-30T08:29:10.093 回答