47

I have class:

class Generic<T : SuperType>() { ... }

And this code is't correct, but cast s to type T:

fun typeCheck(s: SuperType) {
    when(s) {
        is T -> // Do something
    }
}

If use: s as T - this cast will show warning (unsafe cast).

How check that s is T type?

SerjantArbuz
  • 982
  • 1
  • 12
  • 16
andreich
  • 1,120
  • 1
  • 11
  • 28

4 Answers4

54

If you need to check if something is of generic type T you need to to have an instance of Class<T> to check against. This is a common technique in Java however in Kotlin we can make use of an inlined factory method that gets us the class object.

class Generic<T : Any>(val klass: Class<T>) {
    companion object {
        inline operator fun <reified T : Any>invoke() = Generic(T::class.java)
    }

    fun checkType(t: Any) {
        when {
            klass.isAssignableFrom(t.javaClass) -> println("Correct type")
            else -> println("Wrong type")
       }

    }
}

fun main(vararg args: String) {
    Generic<String>().checkType("foo")
    Generic<String>().checkType(1)
}
PålOliver
  • 2,502
  • 1
  • 23
  • 27
Kirill Rakhman
  • 42,195
  • 18
  • 124
  • 148
  • Can you answer for my Question? https://stackoverflow.com/questions/47418161/kotlin-get-type-of-generic-class-without-instance – murt Nov 21 '17 at 16:52
  • great trick! do you know how to make it work when the `Generic` class is abstract? The problem I'm having is instantiating the child object on the `invoke` operator. – ESala Nov 28 '17 at 12:08
  • That won't work so easily. The child class constructor will need to pass the `Class` instance to the super constructor. – Kirill Rakhman Nov 28 '17 at 12:37
18

Generic types are not reified on the JVM at runtime, so there's no way to do this in Kotlin. The warning is correct because the compiler can't possibly generate any instruction that will fail when the cast is done, so the cast is unchecked, meaning that the program may or may not break at some point later instead.

A related feature which might be of use is reified type parameters in inline functions. Classes can't have reified type parameters though, so if you elaborate a bit more on your use case, I can try helping you achieve what you seem to need.

Alexander Udalov
  • 31,429
  • 6
  • 80
  • 66
18

I know that I'm kinda late to this thread, but I just want to recap on the answer provided by Alexander Udalov.

It is, indeed, impossible to determine the type of a generic parameter in Kotlin unless you're using inline functions and declaring the generic type as reified.

Not sure if I'll be able to answer this question entirely and accurately, but I feel like my contribution might still be valuable for someone who is attempting to do just that. So let's say you have a few data classes, and you want to check which type you're dealing with.

You could use a function like that:

inline fun <reified T> checkType() = when (T::class) {
    TypeA::class -> println("TypeA")
    else -> println("Type not recognized")
}

however, functions that call it must also be inline, so you might have to write something like

inline fun <reified T> someOtherFunction(data: T) {
    checkType<T>
}

however, if you cannot allow for an inline function (let's say in an interface!), you can kinda 'cheat' the system by saying, for example

class AmazingTypes {
    inline fun <reified T> checkType(genericParameter: T) = when (T::class) {
        TypeA::class -> println("TypeA")
        else -> println("Type not recognized")
    }
}


fun myAwesomeMethod(someParameter: Any) {
    val amazingClass = AmazingClass()
    amazingClass.checkType(someParameter)
}
2

This is also example.

inline fun <reified T: ApiResponse> parseJson(body: String): T {
    // handle OkResponse only
    val klass = T::class.java
    if (klass.isAssignableFrom(OkResponse::class.java)) {
        return T::class.java.newInstance()
    }
    // handle others
    return gson.from(body, T::class.java)
}
Brownsoo Han
  • 4,549
  • 3
  • 20
  • 20