Search code examples
genericsinlinekotlinreification

Kotlin reified type parameter doesn't smart cast


I was experimenting with setting uninitialized values and was trying to get the following to work. This is mostly a curiosity in the power (and limitations) of reified generics.

I was attempting to provide default values for optional parameters of data classes.

inline fun <reified T> uninitialized(): T = when (T::class) {
  Long::class -> -1L // Type mismatch. Required: T  Found: Long
  String::class -> "" // Type mismatch. Required: T  Found: String
  // and so on...
  else -> throw UnsupportedOperationException("No uninitialized value defined for " + T::class)
}

data class Thing(
    var id: Long = uninitialized(),
    var name: String = uninitialized() // and so on...
)

When when includes is Type clauses, Kotlin has smart casting. In this example, smart casting isn't kicking in so this will not compile.

Any ideas to accomplish something similar?


Solution

  • A smart cast is applied to a specific object after you use is to check its type or compare it with null. In your example, there is no specific object for which you check the type, and nothing to apply the smart cast to.

    However, you can apply manual casts to T, which will work as expected. Here's a working version of your sample function, updated to handle the peculiarities of Kotlin's reflection library which will be fixed in 1.1:

    inline fun <reified T : Any> uninitialized(): T = when (T::class.java) {
      Long::class.javaPrimitiveType, Long::class.javaObjectType -> -1L as T      
      String::class.java -> "" as T
      // and so on...
      else -> throw UnsupportedOperationException("No uninitialized value defined for " + T::class)
    }
    
    data class Thing(
        var id: Long = uninitialized(),
        var name: String = uninitialized() // and so on...
    )
    
    fun main(args: Array<String>) {
        val t = Thing()
        println(t.id)
    }