Generic method for finding instances of classes with 'is' operator - kotlin

I'm new to Kotlin development and trying to create a function that takes an instance of a Exception and a class (e.g RuntimeException) to check if the instance is an instance of the specific class, in Kotlin. The idea is that you have caught a specific type of exception. You then want to traverse up the causes of this exception until you have the specific exception that you were looking for.
fun findExceptionType(currentException : Throwable?, exceptionToFind: KClass<Throwable>): Throwable? {
var _currentException = currentException
while((_currentException!!.cause == null)!!) {
if (_currentException is exceptionToFind) {
return _currentException.cause
}
_currentException = _currentException.cause
}
return null
}
The idea is that it will keep traversing up the exception.cause until either the exception.cause is null, or you have found the type of exception that you were searching for. This seems like something that would already have been implemented and so I'm surprised I'm having to implement this myself.
The reason for this utility function is to avoid having to traverse up all the exception.causes until you find the specific type that you need.
To be more specific:
In Kotlin there is an 'is' operator, for instance you can say if (s is String), however in my function above I want to make it generic by having something like if (s is X) where X is passed into the function. What would be the type of X? At the moment I've used KClass but I'm not sure what the type signature of the is operator is?

I agree with #dyukha. It's pretty convenient to use reified type parameters here. With it you can rewrite your function like:
inline fun <reified T : Throwable> findExceptionType(throwable: Throwable?): T? {
var error = throwable
while (error !is T) {
error = error?.cause
}
return error
}
As generic if (s is X) where X is passed into the function you can use something like this:
(x as? String)?.let { println(it) }

Related

Check if a Kotlin variable is a function

I am currently learning Kotlin and am working through the By Example Guide. In the chapter Functional/Higher-Order Functions it explained how functions themselves can return functions with this example:
fun operation(): (Int) -> Int {
return ::square
}
fun square(x: Int) = x * x
fun main() {
val func = operation()
println(func(2))
}
Since I had previously learned to check the type of a variable in a "when" block, I tried to do the same here. Checking if a variable is of type function.
fun operation(): (Int) -> Int {
return ::square
}
fun square(x: Int) = x * x
fun main() {
val func = operation()
when (func){
is fun -> println(func(2))
else -> println("Not a function")
}
}
But this throws an error "Type expected", I guess because fun in itself is not a type.
I tried searching for things like "kotlin check if variable is function" but all I could find where guides on how to check for primitives or classes, nothing even mentioned functions.
Let's suppose you know nothing about func. (func is of type Any) You can easily check that it is a function by doing:
if (func is Function<*>) {
...
}
Or similarly use is Function<*> in a when branch.
However, this doesn't tell you the number or types of parameters, or the return type. Since you want to call the function with an Int here, it is important that you also check that the function has exactly one parameter of type Int. You can add a number after the word Function to check for a specific number of parameters,
if (func is Function1<*, *>) {
...
}
But that's where the simple stuff stops.
It is very hard to check for parameter types. You cannot just do this:
if (func is Function1<Int, Int>) {
...
}
Because generics are erased, the runtime is not able to distinguish between a Function1<Int, Int> and a Function1<Foo, Bar>, so you cannot check for a specific type parameter using is.
The only way I can think of is unfortunately, reflection.
// JVM only
if (func is Function1<*, *> &&
(func as? KFunction<*> ?: func.reflect())?.parameters?.singleOrNull()?.type == typeOf<Int>()) {
// this is an unchecked cast, which means the runtime won't check it
// but it is fine, because the code above checked it
println((func as Function1<Int, *>)(2))
}
operation can either return a KFunction, like your ::square, or a lambda. If it returns a lambda, the reflect experimental API (You'd need #OptIn(ExperimentalReflectionOnLambdas::class)) is used to turn it into a KFunction.
After we have a KFunction, we can inspect its single parameter (if it has one) and check if it is Int.
So checking for specific types of functions is quite a pain. I would recommend that you change your design to avoid doing this, if you ever find yourself needing to do this.
You can do general check is Function<*> if you just need to find out given func object is a function or not. Also you can do restricted check for more specific function signatures, e.g. check it's a function with N arguments.
In the following code checks are arranged from more specific to less specific:
fun main() {
val func = operation()
when(func) {
is Function2<*,*,*> -> println(func(4)) // function with 2 arguments
is Function1<*,*> -> println(func(3)) // function with 1 argument
is Function<*> -> println(func(2)) // function in general
else -> println("Not a function")
}
}
And the output of this code is 9 because func is both a "Function" & a "Function with 1 argument", but "Function with 1 argument" condition is matched earlier.
P.S. Function22 (f with 22 arguments) is largest built-in into Kotlin

Convert Comparable<*> but get error inferred type is Any? but Nothing was expected

I am trying to convert this piece of java code to kotlin
public int compare1(Comparable c, Object o) {
return c.compareTo(o);
}
to kotlin code:
fun compare1(c: Comparable<*>, o: Any?): Int {
return c.compareTo(o)
}
But get error
Type mismatch: inferred type is Any? but Nothing was expected
Any reason why this error occurs? Thanks
This code in Java shows a warning, because the compiler can't know if provided comparable can compare itself to provided object. Compiler still allows this, but it doesn't guarantee it won't throw an exception at runtime.
Its equivalent in Kotlin is either:
fun compare1(c: Comparable<Any?>, o: Any?): Int {
return c.compareTo(o)
}
With this code you'll have to do unchecked casts when calling the function. Or alternatively:
fun compare1(c: Comparable<*>, o: Any?): Int {
return (c as Comparable<Any?>).compareTo(o)
}
Note it doesn't solve the problem. You can call this function passing an integer and a string and then you will get an exception. So use this code only if the logic of your application guarantees you always pass matching objects to the function.
Even better, try to redesign your code to use generics in a type-safe manner. In that case your function would become:
fun <T> compare1(c: Comparable<T>, o: T): Int {
return c.compareTo(o)
}
This function is type-safe, so it doesn't allow using comparables with incorrect types. It may not work as a direct replacement of your Java function though, as the original function didn't care about the type safety. You may need to redesign other parts of your code to use this function.

Why can't Kotlin auto cast generic type

If you are familiar with unity3d, I'm trying to implement a similar pattern for unity components:
AddComponent<T>();
GetComponent<T>();
So I made this snippet in kotlin:
val map = mutableMapOf<Class<Any>,IComponent>()
fun <T : IComponent> addComponent(component : T){
map.put(component.javaClass,component)
}
fun <T : IComponent> getComponent(klazz : Class<T>): T {
return map.get(klazz)
}
First of all, I have to pass a class to the getComponent method, I can't infer the type from T like C# and I was wondering if there is a way to do this.
And most importantly, why is the method giving me a compile error saying the I'm returning IComponent where T is required, although I did say that T IS an IComponent?
I have to cast to T which is unsafe, this works perfectly fine in C# universe but I'm new to kotlin and I'm wondering if that's possible.
First of all, I have to pass a class to the getComponent method, I can't infer the type from T like C# and I was wondering if there is a way to do this
To be able do something like this the type have to be reified.
In your case it would be something like this:
inline fun <reified T : IComponent> getComponent(): T {
val klazz = T::class.java
// something-something that returns T
}
and most importantly, why is the method giving me a compile error saying the I'm returning IComponent where T is required, although I did say that T IS an IComponent
You said that the type T is IComponent, but not that IComponent is T. And the map contains IComponent as values. Some of them can be T, but there're no guarantee for compiler that they are. So compiler falls with error, and says it isn't sure you will get something of type T out of the map.
So you need to force cast result to the type:
return map.get(klazz) as T // in your case you will have to cast klazz to Class<Any>, btw
To make it castles you should define map as
val map = mutableMapOf<Class<*>,IComponent>()
instead of
val map = mutableMapOf<Class<Any>,IComponent>()
Also, it would be better to use optional type T? for getComponent, in pair with conditional cast as?.

Kotlin: generic cast function parameter

Taking my first steps in Kotlin, I'm struggling to find the correct signature for a function that receives an instance of a known class along with the desired output class and then looks in a map of converter lambdas whether the conversion can be done.
Here's an example for Long:
private fun <T> castLong(value: Long, clazz: Class<out T>): T {
// map lookup removed for simplicity
return when (clazz) {
String::class.java -> { value.toString() }
else -> { throw IllegalArgumentException("Unsupported Cast") }
}
}
Where T is the class of the desired return value - let's say String. One should be able to call castLong(aLongValue, String::class.java) and receive an instance of String.
But the compiler says:
Type mismatch: inferred type is String but T was expected
This seems like it should be possible as it is quite straightforward so far but even playing around with reified and other constructs didn't yield any better results.
It happens because it can't smart cast String to T, you have to manually cast it.
Furthermore, since you said you are taking your first steps in Kotlin, I leave here two other "advices" not strictly related to your question:
you can get the class of T making it reified
the brackets of a case using when aren't necessary if the case is one line
private inline fun <reified T> castLong(value: Long): T {
// map lookup removed for simplicity
return when (T::class.java) {
String::class.java -> value.toString()
else -> throw IllegalArgumentException("Unsupported Cast")
} as T
}

Difference between Any type and Generics in Kotlin

Suppose I have the following function definition.
fun<T> parse(a: Any): T = when (a) {
is String -> a
else -> false
}
I guessed it should be valid. However, the IntelliJ IDEA linter shows a type mismatch error
That being said, I would change the return type of my parse function to Any, right? So that, what is the difference between using Any type and Generics in Kotlin? In which cases should use each of those?
I did read the following question but not understood at all about star-projection in Kotlin due to the fact I am quite new.
Your return type it defined as T, but there is nothing assuring that T and a:Any are related. T may be more restrictive than Any, in which case you can't return a boolean or whatever you provided for a.
The following will work, by changing the return type from T to Any:
fun<T> parse(a: Any): Any = when (a) {
is String -> a
else -> false
}
Any alternate option, if you really want to return type T:
inline fun<reified T> parse(a: Any): T? = when (a) {
is T -> a
else -> null
}
Your example does not use T and thus it's nonsense to make it generic anyways.
Think about this: As a client you put something into a function, e.g. an XML-ByteArray which the function is supposed to parse into an Object. Calling the function you do not want to have it return Any (Casting sucks) but want the function return the type of the parsed object. THIS can be achieved with generics:
fun <T> parse(xml: ByteArray): T {
val ctx: JAXBContext = JAXBContext.newInstance()
val any = ctx.createUnmarshaller().unmarshal(ByteArrayInputStream(xml))
return any as T
}
val int = parse<Int>("123".toByteArray())
val string = parse<String>("123".toByteArray())
Look at the method calls: You tell with generics what type is expected to be returned. The code is not useful and only supposed to give you an idea of generics.
I guessed it should be valid
Why would it be? You return a String in one branch and a Boolean in the other. So the common type for the entire when expression is Any and that's what the compiler (and IDEA) says is "found". Your code also says it should be T (which is "required").
Your generic method should work for any T, e.g. for Int, but Any isn't a subtype of Int and so the code isn't valid.
So that, what is the difference between using Any type and Generics in Kotlin?
This is like asking "what is the difference between using numbers and files": they don't have much in common in the first place. You use generics to write code which can work with all types T (or with all types satisfying some constraint); you use Any when you want the specific type Any.