How can I check for generic type in Kotlin
The problem is that type arguments are erased, so you can’t check against the full type Map, because at runtime there’s no information about those String and Any.
To work around this, use wildcards:
Great! That perfectly works! I just got confused by the example in the documentation: confluence.jetbrains.net/display/Kotlin/Type+casts
I have snippet like this if (it.getSerializable(ARG_PARAMS) is HashMap) > else null . So basically it’s gonna try to cast HashMap to HashMap if I’m checking against generic type. Am I missing something?*,>
I think this is more appropriate way
inline fun tryCast(instance: Any?, block: T.() -> Unit) < if (instance is T) < block(instance) >>
// myVar is nullable tryCast(myVar) < // todo with this e.g. this.canDoSomething() >
Another shorter approach
inline fun Any?.tryCast(block: T.() -> Unit) < if (this is T) < block() >>
// myVar is nullable myVar.tryCast < // todo with this e.g. this.canDoSomething() >
@DaliborFilus nope. This is about Generics and erased types during runtime. If you don´t have to deal with Generics, you can just use as? , correct.
JVM removes the generic type information. But Kotlin has reified generics. If you have a generic type T, you can mark type parameter T of an inline function as reified so it will be able to check it at runtime.
inline fun checkType(obj: Object, contract: T) < if (obj is T) < // object implements the contract type T >>
Could you show an example of how to call checkType() ? I’m not sure what to pass for the second argument.
// Use this value if it is of type T, or else use defaultValue inline fun Any?.useIfTypeOrDefault(defaultValue: T) = if (this is T) this else defaultValue
val map = mapOf("foo" to listOf("cheese"), "bar" to 666) map["foo"].useIfTypeOrDefault>(emptyList()).firstOrNull() shouldBe "cheese" map["bar"].useIfTypeOrDefault>(emptyList()).firstOrNull() shouldBe null map["foo"].useIfTypeOrDefault(-1) shouldBe -1 map["bar"].useIfTypeOrDefault(-1) shouldBe 666
I’m gonna give a workaround solution but I think its clean, kind of
try< (value as Map?)?.let < castedValue ->doYourStuffHere() //using castedValue > >catch(e: Exception) < valueIsNotOfType() //Map>
I have tried the solution above with tryCast> and, I guess, in my specific task in listing with many castings involved it was no so great idea, because it was slowing the performance drastically.
This is the solution I did finally — manually check the entries and call methods, like this:
fun foo() < val map: Map= mapOf() map.forEach < entry ->when (entry.value) < is String -> < doSomeWork(entry.key, entry.value as String) >is Array -> < doSomeWork(entry.key, (entry.value as? Array)?.map < if (it is String) < it >else null >?.toList()) > > > > private fun doSomeWork(key: String?, value: String) < >private fun doSomeWork(key: String?, values: List?)
Type checks and casts
Use the is operator or its negated form !is to perform a runtime check that identifies whether an object conforms to a given type:
Smart casts
In most cases, you don’t need to use explicit cast operators in Kotlin because the compiler tracks the is -checks and explicit casts for immutable values and inserts (safe) casts automatically when necessary:
The compiler is smart enough to know that a cast is safe if a negative check leads to a return:
or if it is on the right-hand side of && or || and the proper check (regular or negative) is on the left-hand side:
// x is automatically cast to String on the right-hand side of `||` if (x !is String || x.length == 0) return // x is automatically cast to String on the right-hand side of `&&` if (x is String && x.length > 0) < print(x.length) // x is automatically cast to String >
Smart casts work for when expressions and while loops as well:
Note that smart casts work only when the compiler can guarantee that the variable won’t change between the check and the usage. More specifically, smart casts can be used under the following conditions:
- val local variables — always, with the exception of local delegated properties.
- val properties — if the property is private or internal or if the check is performed in the same module where the property is declared. Smart casts cannot be used on open properties or properties that have custom getters.
- var local variables — if the variable is not modified between the check and the usage, is not captured in a lambda that modifies it, and is not a local delegated property.
- var properties — never, because the variable can be modified at any time by other code.
«Unsafe» cast operator
Usually, the cast operator throws an exception if the cast isn’t possible. And so, it’s called unsafe. The unsafe cast in Kotlin is done by the infix operator as .
Note that null cannot be cast to String , as this type is not nullable. If y is null, the code above throws an exception. To make code like this correct for null values, use the nullable type on the right-hand side of the cast:
«Safe» (nullable) cast operator
To avoid exceptions, use the safe cast operator as? , which returns null on failure.
Note that despite the fact that the right-hand side of as? is a non-null type String , the result of the cast is nullable.
Generics type checks and casts
Please see the corresponding section in the generics documentation page for information on which type checks and casts you can perform with generics.
How to check generic type in Kotlin?
If you need to check if something is of generic type T you need to to have an instance of Class 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(val klass: Class) < companion object < inline operator fun 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().checkType("foo") Generic().checkType(1) >
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.
That won’t work so easily. The child class constructor will need to pass the Class instance to the super constructor.
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.
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 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 someOtherFunction(data: T) < checkType>
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 checkType(genericParameter: T) = when (T::class) < TypeA::class ->println("TypeA") else -> println("Type not recognized") > > fun myAwesomeMethod(someParameter: Any)
How to check a generic type inside a Kotlin function?
I can easily parse an array of Country with this function. Besides arrays of Country , however, I also have arrays of Cat , Car , Cart , CordlessPhone , etc. Each has their own produce* function transforming a JSONObject to a Kotlin object of that type. To generalize array parsing, I have this function:
fun produceSetOf(array: JSONArray, element: (JSONObject) -> T): Set < val set = mutableSetOf() for (i in 0 until array.length()) set.add(element(array.getJSONObject(i))) return set >
So I can call produceSetOf(jsonArray, produceCountry) on encountering an array whose elements are of type Country . This works well on arrays of Cat , Car , Cart , CordlessPhone too. Problem arises when I see an array of strings. Instead of array.getJSONObject(i) , I have to use array.getString(i) . In effect, I am thinking of introducing another parameterized type to the function above and have it make the call differently:
fun produceSetOf(array: JSONArray, element: (S) -> T): Set < val set = mutableSetOf() for (i in 0 until array.length()) < when (S) < is String ->set.add(element(array.getString(i))) is JSONObject -> set.add(element(array.getJSONObject(i))) > > return set >
Of course, Kotlin does not allow me to do that. Any suggestion how I could do that while maintaining the generality of produceSetOf() and without introducing another layer of abstraction (e.g. an element iterator, or a function transforming an index into String/JSONObject)? Thank you.