3 minutes read

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:

The direction of transfer of variable types on the left to the right from Byte and Short to Double

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 Int to Long:

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 Long to Double:

val bigNum: Long = 100000
val doubleNum: Double = 0.0
val bigFraction = bigNum - doubleNum // 100000.0, Double

Short 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:

  • Byte and Byte

val one: Byte = 1
val two: Byte = 2
val three = one + two // 3, Int
  • Short and Short

val fourteen: Short = 14
val ten: Short = 10
val four = fourteen - ten // 4, Int
  • Short and Byte

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:

  1. If either operand is of type Double, the result is Double.

  2. Otherwise, if either operand is of type Float, the result is Float.

  3. Otherwise, if either operand is of type Long, the result is Long.

  4. 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.

1356 learners liked this piece of theory. 22 didn't like it. What about you?
Report a typo