Reactor/WebFlux 的类型推断和类型变异问题
Problem of type inference and type variance for Reactor/WebFlux
假设有一个接口及其实现 class 为:
public interface InterfaceA {}
public class ClassA implements InterfaceA {
public static Mono<ClassA> getMonoA() {
return Mono.empty();
}
}
然后,对于方法 Mono<InterfaceA> getMonoA()
,以下实现会导致编译错误:
Mono<InterfaceA> getMonoA() {
return ClassA.getMonoA();
}
不变类型 Mono<InterfaceA>
不是 Mono<ClassA>
的超 class 是有道理的,即使 InterfaceA
是 [=] 的超 class 18=],因此 ClassA.getMonoA()
的 return 类型 Mono<ClassA>
与 Mono<InterfaceA>
.
不匹配
但是,以下实现可以正常工作:
Mono<InterfaceA> getMonoA() {
return Mono.just(new ClassA());
}
Mono.just
方法实际上定义为public static <T> Mono<T> just(T data)
。由于基因类型 T
在我的例子中是 ClassA
,该方法应该 return Mono<ClassA>
的类型,这在我看来也应该引发编译错误。
我可以知道是否有一些机制让 Mono.just
方法 returns Mono<InterfaceA>
而不是 Mono<ClassA>
好吗?
我认为看待这个问题的一种方式是编译器如何解释类型 T
。如果你定义
interface InterfaceB {
}
static class ClassB implements InterfaceB {
}
那么下面的代码将无法编译(如预期的那样)
Mono<InterfaceA> getMonoTest() {
return Mono.just(new ClassB());
}
与此类似的消息
type mismatch: cannot convert from Mono<ClassB> to Mono<InterfaceA>
所以编译器会检查类型T
是否对应于方法的return类型。 MonoJust
是 defined 作为通用发布者,您 returning MonoJust<InterfaceA>
而不是 MonoJust<ClassA>
.
final class MonoJust<T> extends Mono<T> ...
final T value;
MonoJust(T value) {
this.value = Objects.requireNonNull(value, "value");
}
另一方面,MonoEmpty
是 defined 作为 Publisher<Object>
:
final class MonoEmpty extends Mono<Object> ....
static final Publisher<Object> INSTANCE = new MonoEmpty();
MonoEmpty() {
// deliberately no op
}
并且您必须定义类型边界:
Mono<? extends InterfaceA> getMonoA()
这就是 java 泛型的工作方式。在第二个示例中,java 编译器可以推断类型。这和定义
是一样的
Mono<InterfaceA> getMonoA() {
return Mono.<InterfaceA>just(new ClassA());
}
但在第一个示例中,编译器无法自动将 Mono<ClassA>
转换为 Mono<InterfaceA>
,但您可以通过应用映射函数来转换它
Mono<InterfaceA> getMonoA() {
return ClassA.getMonoA1().map(Function.identity());
}
这与定义 .<InterfaceA>map(c -> c)
或 .map(c -> (InterfaceA) c)
相同。
或者您可以将方法定义为
public static <T extends InterfaceA> Mono<T> getMonoA() {
return ClassA.getMonoA();
}
类似于 Mono.just
的定义。
由于 type erasure,Mono<InterfaceA>
和 Mono<ClassA>
在运行时都将是 Mono
,因此不会对元素类型进行真正的类型检查。您可以使用在这种情况下有效的不安全转换,但当您尝试访问对象时可能会导致 ClassCastException
Mono<InterfaceA> getMonoA() {
return (Mono) ClassA.getMonoA();
}
这不是 Reactor 或 WebFlux 特有的东西。这同样适用于任何泛型类型。例如,List<InterfaceA>
与 List<ClassA>
.
假设有一个接口及其实现 class 为:
public interface InterfaceA {}
public class ClassA implements InterfaceA {
public static Mono<ClassA> getMonoA() {
return Mono.empty();
}
}
然后,对于方法 Mono<InterfaceA> getMonoA()
,以下实现会导致编译错误:
Mono<InterfaceA> getMonoA() {
return ClassA.getMonoA();
}
不变类型 Mono<InterfaceA>
不是 Mono<ClassA>
的超 class 是有道理的,即使 InterfaceA
是 [=] 的超 class 18=],因此 ClassA.getMonoA()
的 return 类型 Mono<ClassA>
与 Mono<InterfaceA>
.
但是,以下实现可以正常工作:
Mono<InterfaceA> getMonoA() {
return Mono.just(new ClassA());
}
Mono.just
方法实际上定义为public static <T> Mono<T> just(T data)
。由于基因类型 T
在我的例子中是 ClassA
,该方法应该 return Mono<ClassA>
的类型,这在我看来也应该引发编译错误。
我可以知道是否有一些机制让 Mono.just
方法 returns Mono<InterfaceA>
而不是 Mono<ClassA>
好吗?
我认为看待这个问题的一种方式是编译器如何解释类型 T
。如果你定义
interface InterfaceB {
}
static class ClassB implements InterfaceB {
}
那么下面的代码将无法编译(如预期的那样)
Mono<InterfaceA> getMonoTest() {
return Mono.just(new ClassB());
}
与此类似的消息
type mismatch: cannot convert from Mono<ClassB> to Mono<InterfaceA>
所以编译器会检查类型T
是否对应于方法的return类型。 MonoJust
是 defined 作为通用发布者,您 returning MonoJust<InterfaceA>
而不是 MonoJust<ClassA>
.
final class MonoJust<T> extends Mono<T> ...
final T value;
MonoJust(T value) {
this.value = Objects.requireNonNull(value, "value");
}
另一方面,MonoEmpty
是 defined 作为 Publisher<Object>
:
final class MonoEmpty extends Mono<Object> ....
static final Publisher<Object> INSTANCE = new MonoEmpty();
MonoEmpty() {
// deliberately no op
}
并且您必须定义类型边界:
Mono<? extends InterfaceA> getMonoA()
这就是 java 泛型的工作方式。在第二个示例中,java 编译器可以推断类型。这和定义
是一样的Mono<InterfaceA> getMonoA() {
return Mono.<InterfaceA>just(new ClassA());
}
但在第一个示例中,编译器无法自动将 Mono<ClassA>
转换为 Mono<InterfaceA>
,但您可以通过应用映射函数来转换它
Mono<InterfaceA> getMonoA() {
return ClassA.getMonoA1().map(Function.identity());
}
这与定义 .<InterfaceA>map(c -> c)
或 .map(c -> (InterfaceA) c)
相同。
或者您可以将方法定义为
public static <T extends InterfaceA> Mono<T> getMonoA() {
return ClassA.getMonoA();
}
类似于 Mono.just
的定义。
由于 type erasure,Mono<InterfaceA>
和 Mono<ClassA>
在运行时都将是 Mono
,因此不会对元素类型进行真正的类型检查。您可以使用在这种情况下有效的不安全转换,但当您尝试访问对象时可能会导致 ClassCastException
Mono<InterfaceA> getMonoA() {
return (Mono) ClassA.getMonoA();
}
这不是 Reactor 或 WebFlux 特有的东西。这同样适用于任何泛型类型。例如,List<InterfaceA>
与 List<ClassA>
.