I've encountered a strange type mismatch error wit...
# getting-started
r
I've encountered a strange type mismatch error with 1.5.31 and am not sure if it's a bug. Consider the code in the attached snippet. The
compiles
method compiles without issue, but if I save the expression to a variable instead of returning it, it complains about a type mismatch.
This doesn't occur if the return type is just
Response
, but it needs to be RestResponse because that allows you to set status code, headers, etc.
Copy code
// Both of these work
    @GET
    fun compiles2(): Response {
        return if (dto != null) dto
        else ErrResponse("baz")
    }

    @GET
    fun compiles3(): Response {
        val response = if (dto != null) dto
        else ErrResponse("baz")
        return response
    }
c
This might be an issue with how Kotlin handles generics, which is slightly differently than Java does. In the
compiles()
method, the compiler knows the return type of the function, and so it can check whether the generic
RestResponse
returns the specific type of the function, and indeed it does. There is no type inference in here, just simple type-checking. In
fails()
, the compiler must first infer the type of the
response
variable, and since the
RestResponse
is (I'm assuming) a Java class, it tries to reconcile the Java generic variance with Kotlin, and determines it to have
out
variance. But the
fails()
function itself does not have that variance, which is why it won't compile. Adding a type-hint might help the compiler infer the type correctly (
val response: RestResponse<Response> = ...
)
r
Yeah I'm not sure what the actual issue is yet; I'm looking through YouTrack to see if there are any tickets related to this.
In 
fails()
, the compiler must first infer the type of the 
response
 variable, and since the 
RestResponse
 is (I'm assuming) a Java class, it tries to reconcile the Java generic variance with Kotlin, and determines it to have 
out
 variance.
That's a good guess, and what I was initially thinking, but I don't think it's specific to Java. The error persists even when I use a Kotlin 'mock' of RestResponse.
Copy code
data class RestResponse<T>(val status: Int, val entity: T) {
    companion object {
        fun <T> ok(entity: T): RestResponse<T> = RestResponse(200, entity)
        fun <T> status(status: Int, entity: T): RestResponse<T> = RestResponse(status, entity)
    }
}
(Adding an explicit type hint does work though)
i
In Java, all generic types are invariant, so if you construct
RestResponse<Ok>
and
RestResponse<Error>
and assign them to
RestResponse<Response>
even though
Ok
and
Error
are subtypes of
Response
Now, when Kotlin knows the expected type is
RestResponse<Response>
, it can infer types of
RestResponse.ok
and
RestResponse.status
invocations as
ok<Response>(...)
and
status<Response>(...)
, so the result will be assignable to
RestResponse<Response>
.
When there's no expected type, Kotlin infers the most specific types of these operations. When assigning their results to a variable, if infers their common supertype, which is a covariant use site projection of `RestResponse`:
RestResponse<out Response>
You can use IDE action that specifies types explicitly to see what's going on.
r
Makes sense, thanks for the explanation @ilya.gorbunov!