th3ant
th3ant

Reputation: 338

Kotlin Smart Casting generic type by smartcasting argument

I am trying to achieve something analagous to the following toy example:

fun getBoolean() = true
fun getFloat() = 1.0F
fun getInt() = 1
fun getLong() = 1L
fun getString() = "foobar"

fun <T> extract(defaultVal: T): T = when (defaultVal) {
    is Boolean -> getBoolean() as T
    is Float -> getFloat() as T
    is Int -> getInt() as T
    is Long -> getLong() as T
    is String -> getString() as T
    else -> defaultVal
}

Is there a way to achieve this without the unchecked cast as T in the different cases?

Removing the unchecked casts results in a compiler error whereby the return type is the type of the function, and not T. I feel like the compiler should be able to infer T by the defaultVal is ____ in the when statement here in a similar way to smart casting:

fun usesInt(arg: Int) = arg + 1

fun <T> extract(defaultVal: T): T = when (defaultVal) {
    // ...
    is Int -> {
        usesInt(defaultVal)  // defaultVal is smart cast to Int => T is Int
        getInt() as T
    }
    // ...
}

Is this a shortcoming in the smart cast/inference system, or am I missing something here?

Upvotes: 1

Views: 728

Answers (1)

Tenfour04
Tenfour04

Reputation: 93521

First of all, that would be an invalid assumption to make when casting. Consider this:

open class Base
class Derived: Base()

fun getBoolean() = true
fun getBase() = Base()

fun <T> extract(defaultVal: T): T = when (defaultVal) {
    is Boolean -> getBoolean() as T
    is Base -> getBase() as T
    else -> defaultVal
}

Then if you do this:

val extracted: Derived = extract(Derived())

You'll get a ClassCastException because it's trying to cast a Base to a Derived.

I suppose they could make it more sophisticated and check if the classes are open or not, but this would be stepping through a lot of logic. If it was this sophisticated in all areas, I think compilation would be super slow.

Secondly, casting to a generic type is always going to give you an unchecked cast warning since generic types can't be checked at all. The alternative is making the function inline and the type reified. Then it's not an unchecked cast, but you will still get a ClassCastException if you make the mistake above.

Upvotes: 3

Related Questions