Error:
Type mismatch.
Required: Mono<Authentication>
Found: Mono<CustomAuth!>
Code producing error:
fun example(): Mono<Authentication> {
return Mono.just("")
.map { _ -> CustomAuth() }
.onErrorReturn(CustomAuth())
}
where Authentication is from org.springframework.security.core and CustomAuth is:
class CustomAuth : Authentication {
// ...
}
However, it works if we add a cast, e.g.
fun example(): Mono<Authentication> {
return Mono.just("")
.map { _ -> CustomAuth() as Authentication }
.onErrorReturn(CustomAuth())
}
Why is the cast needed?
T
in Mono<T>
is invariant, similar to how ArrayList<Dog>
is not a subtype of ArrayList<Animal>
. Type parameters of classes imported from Java are all invariant.
Theoretically, Mono<T>
can be covariant as far as I know, because Mono<T>
semantically is a "producer" of T
, and not a "consumer" of T
. Kotlin's Flow
, which has similar semantics to Mono
(though it's really more similar to Flux
), indeed has a covariant T
.
example
can be declared to return a Mono<out Authentication>
to make it covariant,
fun example(): Mono<out Authentication> { ... }
But this limits what you can do with the returned value. You cannot do example().onErrorReturn(CustomAuth())
, for example. Again, this theoretically is safe, but because Mono
is written in Java, Kotlin doesn't understand.
As for why adding the cast fixes it, this is because the cast changes the result of the type inference of map
. Without the cast, map
is inferred to return a Mono<CustomAuth>
. By adding the cast, map
is inferred to return Mono<Authentication>
.
Note that removing the onErrorReturn
call, also allows this compile, for a similar reason. By having map
be the last call in the chain, type inference can take into account the "expected type" (Mono<Authentication>
) to infer map
's type parameters. On the other hand, the "expected type" cannot participate in type inference when onErrorReturn
is the last call in the chain, because onErrorReturn
is not generic at all.
You can also explicitly specify the type parameter of map
.
.map<Authentication> { CustomAuth() }