So for example:
var a = true
val test = if (a) -1 else -3.2
I was expecting the Type of the test
should be the most closest intersection of the type-hierarchy i.e. for Int and Double is Number
.
But looking at the IDE, it seems to have a type of {Comparable<{ Double & Int }> & Number}
.
And the weird thing is, I cannot specify it like that (since {} is reserved for creating lambdas), I can only set it to a type of Number
.
And another wierd thing is that if I try some function from Comparable interface, it throws some error:
// Warning at value 2
// The integer literal does not conform to the expected type {Double & Int}
test.compareTo(2)
3.compareTo(-1.1) // possible
2.3.compareTo(100) // possible
// But why not this is possible, while it has inferred type of Comparable?
test.compareTo(2)
Could somebody help in understanding the concept here? And few questions:
&
here means an intersection type (which isn't supported in the Kotlin language itself, but the compiler uses them internally). You can see it mentioned in the (incomplete) specification.
Intersection types are special non-denotable types used to express the fact that a value belongs to all of several types at the same time.
"Non-denotable" means exactly that you can't specify that type. I am not sure but I think the extra {
}
in types are supposed to indicate exactly this.
In particular, Comparable<Double & Int>
means you can only compare test
to something which is both Double
and Int
, but there are no such values. The compiler could probably simplify it to Comparable<Nothing>
.
the most closest intersection of the type-hierarchy i.e. for
Int
andDouble
isNumber
.
It's least upper bound, which is closer to union, not intersection. The specification actually calls it "union types", but that's not the normal usage of that term.
This least upper bound is not Number
because it also takes least upper bound of the Comparable
interfaces which works out to Comparable<Double & Int>
because Comparable
is contravariant:
lub(Int, Double) =
Number & lub(Comparable<Int>, Comparable<Double>) =
Number & Comparable<Int & Double>
This calculation is described under type decaying:
All union types are subject to type decaying, when they are converted to a specific intersection type, representable within Kotlin type system.