Hey folks, I’m facing a `ClassCastException` that ...
# http4k
e
Hey folks, I’m facing a
ClassCastException
that I’m almost sure is being caused by a Kotlin compiler bug but I’m having a hard time confirming it’s not in http4k, particularly due to my lack of familiarity with the lens concepts. Can you help me figure this out so I know who I should report this to? Here’s the smallest reproducer I could get:
Copy code
import org.http4k.core.*
import org.http4k.lens.contentType

fun main() {
	class CustomResponse : Response by Response(Status.OK)

	CustomResponse()
		.contentType(ContentType.APPLICATION_JSON)
		.let { println(it) } // Oddly enough, .let(::println) doesn't cause the bug
}
It throws:
Copy code
java.lang.ClassCastException: class org.http4k.core.MemoryResponse cannot be cast to class ReproducerKt$main$CustomResponse (org.http4k.core.MemoryResponse and ReproducerKt$main$CustomResponse are in unnamed module of loader 'app')
at ReproducerKt.main(Reproducer.kt:11)
I’m running it with Kotlin 2.0.0 and I was also able to reproduce it with 1.9.21 to ensure it’s not related to K2. http4k version is 5.21.1.0
d
This is because the
with
statement is taking
MemoryResponse
as a generic and not
Response
. You can fix it by upcasting to the Response
Copy code
fun main() {
    class CustomResponse : Response by Response(Status.OK)

    val resp: Response = CustomResponse()
    resp
        .with(CONTENT_TYPE of ContentType.APPLICATION_JSON)
        .let { println(it) }
}
alternatively, this will do what you want without problems:
Copy code
class CustomResponse : Response by Response(Status.OK) {
    fun with(vararg modifiers: (Response) -> Response) = (this as Response).with(*modifiers)
}
e
Thanks, I applied it to my non-reproducer code and it worked. However, I don’t get why it’s inferring
MemoryResponse
given
Response.Companion.invoke
returns
Response
.
d
It's not that - the val resp without the cast would be of type CustomResponse. It's the with function which is blowing up when applying the lens.
e
Can we consider that as a bug?
(not a rethorical question — really trying to understand)
d
Marginal - in the past we haven't really considered the Response to be user-extendable. What exactly is the purpose of you extending it with CustomResponse? (It might be that there is a more idiomatic way to achieve the same thing.)
e
I want to return this custom response, which contains some additional data, over to one of my filters. There, I check the type of the response and if it’s a custom response, I add some additional headers, etc. My type check is smart casting the response to this extended type and that’s where it blows up. I read briefly about
RequestContexts
and that looks like an alternative but I wanted to try my approach since it’s simpler.
d
yeah- RequestContexts is the idiomatic way to do this TBH.
e
Gotcha. Thanks for the help.