我怎样才能实现一个接受 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>
,其中 S
是 T
的超类型。您可以通过以下任一方式执行此操作:
- 转换——你知道这是安全的,因为
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);
}
并像这样调用(使用问题代码中 cri
、crn
和 t
的值):
foo3(cri, t); // compiles
foo3(crn, t); // compiles
在下面的示例中,我可以将 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>
,其中 S
是 T
的超类型。您可以通过以下任一方式执行此操作:
- 转换——你知道这是安全的,因为
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);
}
并像这样调用(使用问题代码中 cri
、crn
和 t
的值):
foo3(cri, t); // compiles
foo3(crn, t); // compiles