Eugene T
Eugene T

Reputation: 353

Reason why type inference failed in Kotlin


Could someone please help me to wrap my head around the Kotlin compiler behavior?
The intention of the program is to convert the input string to a corresponding type(BMsg or CMsg) and print the result in a console.
However, I receive the following message from the compiler:

"Type inference failed. Not enough information to infer parameter T ..."

import com.fasterxml.jackson.module.kotlin.jacksonObjectMapper

abstract class classA<T> {
    fun handle(body: String) {
        val result = convertToSpecificMessageType(body) // This line fails
        printResult(result)
        otherImportantStuff()
    }

    abstract fun otherImportantStuff()

    private inline fun <reified T : Any> String.toPayloadObject(): T =
            jacksonObjectMapper().readValue(this, T::class.java)

    private inline fun  <reified T : Any> convertToSpecificMessageType(body: String): T = body.toPayloadObject()
    private fun printResult(result: T) = println("result = $result")
}

class classB : classA<BMsg>() {
    override fun otherImportantStuff() = print("important stuff of class B")
}

class classC : classA<CMsg>() {
    override fun otherImportantStuff() = print("important stuff of class C")
}

sealed class Msg
data class BMsg(val x: String) : Msg()
data class CMsg(val y: Int) : Msg()

fun main() {
    classB().handle("{\"x\" : \"aaa\"}")
    classC().handle("{\"y\" : 5}")
}    

Despite I found a workaround - declaring convertToSpecificMessageType method as an abstract and override in classB/classC, I'm wondering (1) what is the reason which makes the compiler to complain (2) if there is a way to fix the problem without overriding convertToSpecificMessageType? Thank you in advance!

P.S. kotlin version = '1.3.21'

Upvotes: 1

Views: 1986

Answers (1)

Erwin Bolwidt
Erwin Bolwidt

Reputation: 31279

By declaring <reified T : Any> in front of your methods (toPayloadObject and convertToSpecificMessageType) you're introducing a new type parameter T for your methods that has no relation to the type parameter of classA with the same name T.

The method-local declaration of T shadows the T on classA.

As a result, when you do val result = convertToSpecificMessageType(body) - there is no context at all that Kotlin can use to infer the type from. Although you're in a method in classA, the T from classA is not used by method convertToSpecificMessageType.

The solution?

You can't use a reified type on a class so either:

  • you can remove the <reified T : Any> parts and pass a class object to the methods
  • or you keep it there but you provide context when you invoke the method as in val result = convertToSpecificMessageType<MyType>(body) or val result : MyType = convertToSpecificMessageType(body)

Upvotes: 1

Related Questions