Skip to content
Advertisement

Problem of type inference and type variance for Reactor/WebFlux

Let’s say there is an interface and its implement class as:

public interface InterfaceA {}

public class ClassA implements InterfaceA {

    public static Mono<ClassA> getMonoA() {
        return Mono.empty();
    }

}

And then, for the method Mono<InterfaceA> getMonoA(), the following implementation causes a compile error:

Mono<InterfaceA> getMonoA() {
    return ClassA.getMonoA();
}

It makes sense that the invariance type Mono<InterfaceA> is not the super class of Mono<ClassA> even if InterfaceA is the super class of ClassA and therefore the return type of ClassA.getMonoA() which is Mono<ClassA> does not match the Mono<InterfaceA>.

However, the following implementation works properly:

Mono<InterfaceA> getMonoA() {
    return Mono.just(new ClassA());
}

The Mono.just method is acturally defined as public static <T> Mono<T> just(T data). As the geneic type T in my case is ClassA, the method should return the type of Mono<ClassA> which should also raise a compile error in my mind.

May I know if there are some mechanisms let Mono.just method returns Mono<InterfaceA> instead of Mono<ClassA> please?

Advertisement

Answer

This is the way how java generics works. In the second example java compiler could infer type. This is the same as defining

Mono<InterfaceA> getMonoA() {
   return Mono.<InterfaceA>just(new ClassA());
}

But in the first example compiler could not cast Mono<ClassA> to Mono<InterfaceA> automatically but you could cast it by apply a map function

Mono<InterfaceA> getMonoA() {
     return ClassA.getMonoA1().map(Function.identity());
}

that is the same as defining .<InterfaceA>map(c -> c) or .map(c -> (InterfaceA) c).

or you could define your method as

public static <T extends InterfaceA> Mono<T> getMonoA() {
    return ClassA.getMonoA();
}

similar how Mono.just is defined.

Due to type erasure, both Mono<InterfaceA> and Mono<ClassA> will be Mono during runtime, so there will be no real type check regarding the element type. You could use unsafe cast that will work in this case but potentially could result in ClassCastException when you try to access the object

Mono<InterfaceA> getMonoA() {
    return (Mono) ClassA.getMonoA();
}

It’s not something specific to Reactor or WebFlux. The same is valid for any generic types. For example, List<InterfaceA> vs List<ClassA>.

User contributions licensed under: CC BY-SA
9 People found this is helpful
Advertisement