Type comparison / match in kotlin - kotlin

I use when with type cases as mentioned in the code below. It is needed for me in reflection where I check the type of the property/field and set or cast value accordingly.
In this example, Double? property type is not matching the case Double::class.java . If I change the property type to Double without ? then everything works as expected. In this case how do I solve this for nullable properties declared with ?
valueToSet = when(member?.getter?.returnType?.javaType) {
Date::class.java -> {
DateUtil.asDate(LocalDateTime.parse(value))
}
Integer::class.java -> value.toInt()
Double::class.java -> value.toDouble()
else ->value.toString();
}

javaType gives you a Type instance, not a Java Class instance. Use classifier instead and compare against the KClasses. You can get returnType directly from the KProperty without going through the getter.
valueToSet = when(member?.returnType?.classifier) {
Date::class -> {
DateUtil.asDate(LocalDateTime.parse(value))
}
Integer::class -> value.toInt()
Double::class -> value.toDouble()
else -> value.toString();
}

Double? corresponds to java.lang.Double, Double to double. Compare against both types, e.g.:
Double::class.java, java.lang.Double::class.java -> value.toDouble()
or separate if you want to treat them differently.
(The changes suggested by Tenfour04 are a good idea as well.)

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

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
}

Operator overloading as extension function

In my game I have two players and so I defined a typealias of pair that should indicate that I have two things of the same type and .first belongs to player one and .second belongs to player two:
typealias PlayerPair<A> = Pair<A, A>
Also I have defined this enum class
enum class PlayerNumber {
One,
Two
}
Now i would like to add an operator (as extension function) to access the elements like this
myPair[Player.One]
That is my approch but it doesn't work
operator fun PlayerPair.get(i: PlayerNumber) = when (i) {
PlayerNumber.One -> PlayerPair.first
PlayerNumber.Two -> PlayerPair.sexond
}
Adding an else Branch removes one error, but I don't understand why it is necessary.
The other error is 'One type argument expected for typealias PlayerPair = Pair'.
But I can't figure out what this means in this context.
You can create a generic get function, you just have to specify a type parameter, and that you're extending a PlayerPair<T>:
operator fun <T> PlayerPair<T>.get(i: PlayerNumber): T = when (i) {
PlayerNumber.One -> this.first
PlayerNumber.Two -> this.second
}
Inside the operator, you can refer to the instance of PlayerPair<T> as this, which you can also use implicitly:
operator fun <T> PlayerPair<T>.get(i: PlayerNumber): T = when (i) {
PlayerNumber.One -> first
PlayerNumber.Two -> second
}

Type name as value?

Can somebody point me to the documentation explaining what the following means? Especially, I'd like to know why String and Int can be used as shown.
val a: Unit = { _: Any -> String }(Int)
Initially, I had written this:
val a: Unit = { x: Any -> x.toString() }(Unit)
In both cases, I've failed to find the right documentation.
Let's break down the first line.
{ _: Any -> String }
This is a lambda. It takes one parameter, that's marked as never used by giving it the name _. The parameter of this type is specified as Any. The lambda returns String, which refers to the (completely empty) companion object inside the String class. The type of this lambda is therefore (Any) -> String.Companion.
{ _: Any -> String }(Int)
This lambda is invoked by the parentheses after it, passing in Int, which again refers to the companion object inside that class (this one isn't empty, it contains constants). This works, because of course, Int.Companion is a subtype of Any, the expected parameter type.
val a: Unit = { _: Any -> String }(Int)
Finally, the declaration of a with its explicit Unit type forces the lambda to change its inferred type from (Any) -> String.Companion to (Any) -> Unit, so it can return the expected explicit type after it's invoked.
The second line is more of the same, only simpler without the companion objects.
Type name as value?
String and Int are the companion objects (sort of the equivalent to Java's static) of the String and Int classes. The name of a class is synonymous with the name of its companion object, i.e. Int more or less is the same as Int.Companion, etc.
{ _:Any -> String } and { x:Any -> x.toString() } are both lambdas of type (Any) -> String.Companion and (Any) -> String, but since the result of the function call is assigned to a, which is of type Unit, these functions are both inferred to return Unit.
Basically, after type inference:
{ _:Any -> String } is a (Any) -> Unit
{ x:Any -> x.toString() } is also a (Any) -> Unit
This type corresponds to the void type in Java.
Documentation: Unit
So these functions both effectively do nothing.
The first function (of type (Any) -> Unit takes one argument (which is unused) and returns String.Companion.
In this case, it is called with Int.Companion... but the argument is unused, so it doesn't matter.
You could have written:
val lambda: (Any) -> Unit = { _: Any -> String }
val a: Unit = lambda(Int)
The second function calls toString on its parameter, which is in this case Unit. The string representation of Unit is defined as "kotlin.Unit", but since it is assigned to a the function's return type is inferred to be Unit, so the function doesn't return anything useful.
These functions are both quite useless as they return Unit (i.e. void, nothing). What do you want to accomplish?
You defined a lambda { _: Any -> String } of type (Any)-> String.Companion and called it with the Int.Companion object. It always returns the String.Companion object. Does not make much sense. The following might be more readable:
val func = { _: Any -> String.Companion }
val res = func(Int.Companion)
Note that Int and Int.Companion are used interchangeably here (also applies for the String one).
Information about companion objects can be found in the documentation.

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.