Does the compiler ignore explicitly declared types?


#1

I have a situation where val someList: List<String> actually contains elements of a different type instead of only String. Is this normal ?

To elaborate: I’m using Spring Data JPA. I have a function with @Query, that had a List<String> as a return type. Because of a mistake I made in the query, instead of this function returning a List<String> I ended up with a list containing SomeEntity from calling this function.

It looked something like this:

@Query("bad query")
fun findEntityNames(): List<String>

...

val entityNames: List<String> = findEntityNames()
// here entityNames actually contains List<SomeEntity> and not List<String>, still no exception

It kept working fine until I tried to use that value in a stream, then I got: java.lang.IllegalArgumentException: Parameter value element <omitted> did not match expected type [java.lang.String (n/a)]
The rest of the stacktrace contains only hibernate references, nothing for Kotlin, but it seemed odd that I can declare a specific type for a value and it will be ignored.

When debugging, I saw the type of entityNames was a raw ArrayList.

Did I do something wrong, or should I report this as a bug ?


#2

Well, no (except for your “bad query”). This is actually a known issue which can not be solved on the JVM.
Kotlin is restricted to the features of the JVM bytecode. Due to legacy reasons generic types don’t exist in the bytecode. That means that List<String> gets compiled down to just List. (If you want to know more about this search for java type erasure).
Normally this is not a problem. The compiler ensures that whenever you access a List<String> you can only add Strings to it. There is however one exception: Reflection.
Reflection allows you to go around the compiler and access elements of the program in a less save way. You can for example read/write fields by just knowing the name of it (e.g you have the field name stored in a String). This can be extremely powerful (e.g serialization for arbitrary types can be implemented using this) but can also lead to hard to find bugs.

This is correct and not a problem. List is just an interface and ArrayList is the most basic implementation of it, e.g every time you call listOf(...) kotlin creates an ArrayList.
The advantage of using List instead of ArrayList as the type is that you can easily switch out the implementation without having to change any code that uses the list. This can be useful when you later notice that another list implementation gives you better performance.