在以下示例中,我可以将Consumer<Optional<Integer>
传递给foo
,但不能传递给Consumer<Optional<Number>>
。另一方面,我可以将任一类型传递给foo2
,但是我不能从方法体中调用使用者的accept方法。有没有办法更改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
}
}
答案 0 :(得分:1)
原因是Optional
从类型系统的角度来看并不特殊:我们知道Optional
只有提供者方法(Optional.get()
)并且它没有消费者方法(如Optional.set(T)
);但是编译器没有。
所以,它赢了的编译器让你传递Optional<Integer>
需要Optional<Number>
的地方:它阻止你调用那个神秘的set
方法,万一您传递的是Double
而不是Integer
。
解决此问题的唯一方法是将Optional<T>
更改为Optional<S>
,其中S
是T
的超类型。您可以通过以下任一方式执行此操作:
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);
}
并像这样调用(使用问题代码中的cri
,crn
和t
值:
foo3(cri, t); // compiles
foo3(crn, t); // compiles