Cast reflection "Type" to a List<class> in Kotlin - kotlin

In my Kotlin code, I have a variable that is the Type interface from
java.lang.reflect
var type: Type
But I need to cast this to:
List<UserInfo>
If I was not casting to a List, I would just do this:
var type = UserInfo::class.java
and this works. But I don't know how to cast it using a List. The closest I found is this:
var type = Array<UserInfo>::class.java
This would compile if my UserInfo was an Array but it's a List.

The issue (as #Miha_x64 says) is type erasure.
The Java Virtual Machine knows nothing about type parameters.  So although the source specified a List<UserInfo>, it compiles down to a plain List.
So this works:
var type = List::class.java
(Your Array example works because arrays are a special case: they're directly supported in the JVM, and keep their types at runtime.)
Java's use of type erasure is at least partly for historical reasons; when generics were added to Java 5, they wanted to preserve compatibility with existing source and bytecode.  For all the gory details (much of which is inherited by Kotlin), see here.

Maybe its late. Try to use KTypeProjection and createType() with KClass instead. Then get the value in KType or convert it back to java Type.
val kClass = YourClass::class
val kTypeProjection = KTypeProjection.invariant(entity.starProjectedType)
val kType = List::class.createType(listOf(kTypeProjection))
val type = kType.javaType
result:
kType: kotlin.collections.List<YourClass>
type: java.util.List<YourClass>

Related

why List<String> can cast to List<Int> in Kotlin?

why below code can run?
fun main(args: Array<String>) {
val somePair: Pair<Any?, Any?> = "items" to listOf("1","2a")
var W=somePair.second as List<Int>
println(W)
}
output:[1, 2a]
"2a" is not Int , but the type of W is List, why no exception throw?
Compiling your code gives the following warning:
Unchecked cast: Any? to List<Int>
This literally means that the cast will not throw an exception in all cases. If you choose to ignore this warning, you should be prepared for this behaviour to happen.
Now as to why this actually runs fine, keep in mind that generic types are erased at runtime. This means that a List<String> or a List<Int> is actually just a raw List at runtime, the information about the element type is lost.
The point of the generics is mostly to help when compiling the code: the compiler prevents you from adding elements of the wrong type to the list, and it helps you when you read elements from the list by giving you a value with the proper type (no cast required).
Casts, however, operate at runtime. They check the runtime type of an object against a given type. The type List<Int> that you cast to is a generic type, and the compiler warns you because at runtime it will only see the raw interface List.
More concretely, somePair.second as List<Int> compares the runtime type of somePair.second (which is ArrayList here) with the raw List interface - and that is a match, so no exception.

how to convert Java Map to read it in Kotlin?

I am facing some very basic problem (that never faced in java before) and might be due my lack of knowledge in Kotlin.
I am currently trying to read a YML file. So Im doing it in this way:
private val factory = YamlConfigurationFactory(LinkedHashMap::class.java, validator, objectMapper, "dw")
Best on Dropwizard guide for configurations.
https://www.dropwizard.io/1.3.12/docs/manual/testing.html
So later in my function I do this"
val yml = File(Paths.get("config.yml").toUri())
var keyValues = factory.build(yml)
When using my debugger I can see there is a Map with key->values, just as it should be.
now when I do keyValues.get("my-key")
type inference failed. the value of the type parameter k should be mentioned in input types
Tried this but no luck
var keyValues = LinkedHashMap<String, Any>()
keyValues = factory.build(yml)
The YamlConfigurationFactory requires a class to map to, but I dont know if there is a more direct way to specify a Kotlin class than with the current solution +.kotlin, like
LinkedHashMap::class.java.kotlin
Here it also throws an error.
Ideas?
Well, this is a typical problem with JVM generics. Class<LinkedHashMap> carries no info on what are the actual types of its keys and values, so the keyValues variable always ends up with the type LinkedHashMap<*, *> simply because it can't be checked at compile time. There are two ways around this:
Unsafe Cast
This is how you would deal with the problem in standard Java: just cast the LinkedHashMap<*, *> to LinkedHashMap<String, Any> (or whatever is the actual expected type). This produces a warning because the compiler can't verify the cast is safe, but it is also generally known such situations are often unavoidable when dealing with JVM generics and serialisation.
YamlConfigurationFactory(LinkedHashMap::class.java, ...) as LinkedHashMap<String, Any>
Type Inference Magic
When using Kotlin, you can avoid the cast by actually creating instance of Class<LinkedHashMap<String, Any>> explicitly. Of course, since this is still JVM, you lose all the type info at runtime, but it should be enough to tell the type inference engine what your result should be. However, you'll need a special helper method for this (or at least I haven't found a simpler solution yet), but that method needs to be declared just once somewhere in your project:
inline fun <reified T> classOf(): Class<T> = T::class.java
...
val factory = YamlConfigurationFactory(classOf<LinkedHashMap<String, Any>>(), ...)
Using this "hack", you'll get an instance of LinkedHashMap directly, however, always remember that this is just extra info for the type inference engine but effectively it just hides the unsafe cast. Also, you can't use this if the type is not known at compile type (reified).

How do I conserve type using Observable.zip?

I'm trying to apply Observable.zip to a list of observables. It works but I would like it to conserve the type information. The problem happens with the snippet below:
val observable1 = Observable.fromArray(1, 2, 3)
val observable2 = Observable.fromArray(1, 2, 3)
// result is a Array<Any>. Could it be an Array<Int> instead ?
val result = Observable.zip(listOf(observable1, observable2), {a -> a}).blockingFirst()
It feels like zip should be able to know that my items are of type Int. In this very specific case, I could certainly cast but I also have more complex types and would feel much better if I could conserve type. Do I miss something ?
Unfortunately, the Java (and Kotlin) type system doesn't let you (or us library writers) do such type preservation; you have to manually cast back the a Object[] elements to their respective type.
This is done for you with the 2-9 argument zip() overloads and the zipWith instance operator.
The underlying problem is that one can't create a generic array as well as the type erasure itself. If we allowed Function<T[], R> in the signature, we would still have to create new Object[] for the invocation. However, your Function<Integer[], R> implementation would try to cast Object[] into Integer[] which is not allowed and fails with ClassCastException.
This is also described in the zip javadoc:
Note on method signature: since Java doesn't allow creating a generic array with new T[], the implementation of this operator has to create an Object[] instead. Unfortunately, a Function<Integer[], R> passed to the method would trigger a ClassCastException.

Convert Java Collection.toArray to kotlin

Please help me convert this java code to kotlin, I try many times but I can't
ObjectStream<DocumentSample> bin
= ObjectStreamUtils.createObjectStream(categoryStreams.toArray(new ObjectStream[0]));
In Kotlin, you may use toTypedArray() to get an Array from a List:
categoryStreams.toTypedArray() //Array<ObjectStream<DocumentSample>>
Then, you can use spread operator to prefix the array and pass it to ObjectStreamUtils.createObjectStream():
val bin = ObjectStreamUtils.createObjectStream<DocumentSample>(*categoryStreams.toTypedArray())
Assuming the categoryStreams object is one of the Collection type(List, Set, etc.), you can call kotlin's extension method toTypedArray on it to get an array of the ObjectStream:
This should work:
val bin = ObjectStreamUtils.createObjectStream(categoryStreams.toTypedArray())
If the method accepts vararg of ObjectStream, then you can use spread operator * to spread array to vararg:
val bin = ObjectStreamUtils.createObjectStream(*categoryStreams.toTypedArray())
To convert Java file to Kotlin
On the main menu, point to Code menu.
Choose Convert Java File to Kotlin File.
Done.
In your case it converts to:
val bin = ObjectStreamUtils.createObjectStream(categoryStreams.toArray(arrayOfNulls<ObjectStream>(0)))
var bin = ObjectStreamUtils.createObjectStream(categoryStreams.toArray(arrayOfNulls(0)))
the above code is converted your code to kotlin.
It sounds like a type inference problem. Try the code below. I add the declaration for categoryStreams. Not sure how it is declared in your code. But this is a reasonable declaration from looking at the code on github.
val categoryStreams = arrayListOf<ObjectStream<DocumentSample>>()
val bin = ObjectStreamUtils.createObjectStream(categoryStreams.toArray(arrayOfNulls<ObjectStream<DocumentSample>>(0)))
The idea is to provide the correct type for inference (which the converter neglected to provide). The original java did not infer the type, it was an explicit cast.

Cast Any to Array in Kotlin

I'm initializing a class by loading data from a Map<String, Any> in Kotlin. As this Map is gleaned directly from JSON, I don't know for certain that any given key exists, or that its value is of the type I expect. To unpack this Map safely I'm doing the following, which appears to work perfectly:
a = rawData["A"] as? String ?: ""
Some of this data is in further nested JSON, which I'm unpacking to Arrays; I've tried to do this in the same way:
b = rawData["B"] as? Array<String> ?: arrayOf<String>()
However, when I attempt this using an array (as above) IntelliJ kicks up a fuss, saying
Warning:(111, 30) Kotlin: Unchecked cast: Any? to Array<String>
Is this just the IDE getting itself in a twist or is this method genuinely unsafe for Arrays despite being seemingly perfectly safe for other types?
For any future readers of this question, to expand on the accepted answer with a solution:
To safely cast Any to an array of a particular type in Kotlin, you have to first cast to an untyped array (see zsmb13's answer above for why), and then filter that array to the desired type.
For example, to cast input: Any to an array of String instances, you would call:
val inputAsArray = (input as? Array<*>)?.filterIsInstance<String>()
I was ready to call this a bug, because Array is a reified type, meaning its generic parameter can actually be checked at runtime (unlike a List, for example). I've tried looking to see if it's been filed yet, and it turns out the compiler is actually right to show you a warning. As you can see in the response to this issue, there's still a nullability problem with casts of this kind.
val a = arrayOf("foo", "bar", null) as Array<String>
println(a[2].length)
Arrays like the one in this example are successfully cast (using as, they don't throw an exception, using as?, they don't return null), however, the cast can not ensure that this is an Array<String>, only that it's an Array<String?>.
This means that you can later read null values from a variable that is typed as an Array<String> after the cast, with no further warnings from the compiler.