I stumbled upon a bug in my code that I thought would be noticed & prevented by the compilation:
val ints: List<Int> = listOf(1, 2, 3)
val uuids: MutableList<UUID> = mutableListOf(UUID.randomUUID())
// No compilation error here:
val result = uuids.intersect(ints)
// Compilation error here, as expected
uuids.add(1)
IJ mentions that result is of type Set<{Comparable<*> & Serializable}>
And intersect signature seems correct:
public infix fun <T> Iterable<T>.intersect(other: Iterable<T>): Set<T> {
My bug was that I just checked wether the intersection was empty, thus not seeing that by comparing incompatible objects, that would always be true.
So do I miss something obvious? Thanks
I agree that it's a bit surprising that it is valid to call
intersectwithListobjects of two different types. However, this is an inevitable consequence of Kotlin's type inference system and the type projection onListobjects.Type inference
No type is specified in your call
uuids.intersect(ints)so Kotlin checks to see if a type exists that makes the call valid, and for the most specific type that the output can be. The answer to that there is such a type so the call is valid; and that type is aSetofComparable<*> & Serializableobjects. Thus Kotlin makes that type the generic typeTfor the call.This is the case because both
List<UUID>andList<Int>are both subtypes ofList<Comparable<*> & Serializable>(not that you can write that intersection type in Kotlin1). In turn, this is true because bothUUIDandIntare subtypes of bothComparable<*>andSerializable, andLists are covariant2.I don't see any way the signature of the function
intersectcould be written to avoid this problem, barring some special Kotlin annotation to change the usual type inference algorithm.Invariant types
Instead, if you have a function of the form:
and
GenericTypewas invariant (ie no type projection) then you will get an error if you try to call it with an argumentGenericType<S>(whereSis not the same asT). I suspect this case you would more often come across, and this would not give surprising results.Specifying a type
If you specify the type for your call, either as the type of
resultor the generic type of the call asUUID(the type you were surely expecting), then you do get an error:In such cases, there is no type inference to do and the compiler can immediately see there is a mismatch.
1 See the YouTrack issue for the feature request
2 ie
outprojection types, meaning aList<A>is a subtype ofList<B>ifAis a subtype ofB