Quite a few generics related questions here recent...
# getting-started
d
Quite a few generics related questions here recently so I will add my own as well 🙂 I'm trying to create an extension on an interface/class from Spring's WebTestClient and cannot get the types right:
Copy code
// this is (basically) from WebTestClient
class RequestHeadersSpec<S : RequestHeadersSpec<S>> {
    fun header(headerName: String, headerValue: String): RequestHeadersSpec<*> = this
}

// this is my extension
fun <S : RequestHeadersSpec<S>> S.customHeader(headerValue: String): RequestHeadersSpec<*> =
	header("custom-header", headerValue)

fun main() {
    val spec = RequestHeadersSpec()
    spec.customHeader("value")
}
Basically I want the
customHeader
extension to have the same receiver and output type as
header
. I'm getting "receiver type mismatch" on the call site, however. https://play.kotlinlang.org/#eyJ2ZXJzaW9uIjoiMi4wLjIwLVJDIiwicGxhdGZvcm0iOiJqYXZhIiwiYX[…]BlYygpXG4gICAgc3BlYy5jdXN0b21IZWFkZXIoXCJ2YWx1ZVwiKVxufSJ9
s
I'd be tempted to just use
apply
, and let Spring worry about the awkward generics
Copy code
fun <T : RequestHeadersSpec<*>> T.customHeader(headerValue: String): T =
  apply { header("custom-header", headerValue) }
plus1 1
d
Ah, so you also find the types not entirely correct/convenient? Out of curiosity, what's the exact root cause of the awkwardness in your opinion and how to avoid it?
s
Just type variance I think. This works:
Copy code
fun <S : RequestHeadersSpec<out S>> S.customHeader(headerValue: String): RequestHeadersSpec<*> =
  header("custom-header", headerValue)
But trying to make a 'self type' with generics is always a mess, and if you're already using extension functions,
apply
is an easy way out.
d
Hmm, the
apply
way works but I don't exactly understand how: • first, you need to use wildcard base type
RequestHeadersSpec<*>
• second, what exactly
apply
brings to the game wrt types?
Ah, I can answer the second question myself 🙂 By using
apply
we are completely ignoring the problematic typing of
headers
and just returning the receiver type