You already know how to perform type conversion. There are more advanced aspects of it: for example, you know that we cannot assign a variable of Int type to a Long variable. But what happens if we calculate the sum of Int and Long variables? In this case, the type is inferred from the context.
Type coercion
In such cases, the compiler automatically sets all components (it's called type coercion) and the result type to the widest type in the expression. The picture below illustrates the direction of this casting:
Since the type of the result is wider than the previous type, there is no loss of information.
Type coercion is rare in Kotlin. It works only with numbers and strings.
Examples
The theory looks pretty clear, so let's take a look at some examples of type coercion.
from
InttoLong:
val num: Int = 100
val longNum: Long = 1000
val result = num + longNum // 1100, Long
Although result is just 1100, it is the sum of Long and Int variables, so the type is automatically cast to Long. If you try to declare a result as Int, you get an error because you cannot assign the value of Long type to an Int variable. You can assign only an Int value or an integer number to a variable of Int type.
from
LongtoDouble:
val bigNum: Long = 100000
val doubleNum: Double = 0.0
val bigFraction = bigNum - doubleNum // 100000.0, DoubleShort and Byte types
You can see how the result of an expression with variables of different types is automatically cast to the widest type. However, the Byte and Short types are unusual in this respect. If you need to do some calculations with these types, the result of the calculation is Int:
ByteandByte
val one: Byte = 1
val two: Byte = 2
val three = one + two // 3, Int
ShortandShort
val fourteen: Short = 14
val ten: Short = 10
val four = fourteen - ten // 4, Int
ShortandByte
val hundred: Short = 100
val five: Byte = 5
val zero = hundred % five // 0, Int
So what should we do if we want to sum two Byte variables and get a Byte result? Well, in this case, you must manually perform type conversion:
val one: Byte = 1
val five: Byte = 5
val six = (one + five).toByte() // 6, Byte
Remember that Byte can store data in the range -128.. 127.
Look at the example below of how type overflow works:
fun main() {
val a: Byte = 120
println((a + a).toByte()) // prints -16 because 120+120 > 127
}
Conclusion
To sum up, if you have an expression with different numeric types, use these rules to know the type of the result:
If either operand is of type
Double, the result isDouble.Otherwise, if either operand is of type
Float, the result isFloat.Otherwise, if either operand is of type
Long, the result isLong.Otherwise, the result is
Int.
Type coercion does not occur when a value is put into the variable. For example, val longValue: Long = 10.toInt() is incorrect, because 10 is Int and longValue requires the Long type.
The compiler automatically deduces the type of expression. It helps you omit type conversion in simple cases, but you need to understand how it works to prevent confusion and errors.