Why conversion is explicit
Every Kotlin value has a type, and Kotlin does not silently turn one type into another. The compiler rejects this:
val age = 25
val d: Double = age
error: initializer type mismatch: expected 'Double', actual 'Int'.
Even Int to Double, a safe change in principle, has to be written out. When you need a value of a different type, you ask for it with a conversion function. Each one returns a new value and leaves the original alone.
The conversion functions
x.toString() // a number (or other value) to String
s.toInt() // String to Int
s.toDouble() // String to Double
x.toInt() // Double to Int
Some examples with their results:
val score = 42
val scoreStr = score.toString() // "42"
val weight = 70.8
val weightInt = weight.toInt() // 70
val count = "100"
val countInt = count.toInt() // 100
These are method calls on the value, which is why the dot comes first. The name says the target type: toInt() produces an Int.
Numbers to text
toString() works on any value. The result looks the same when printed, but it behaves as text: 25.toString() + 1 gives the string 251, while 25 + 1 is the number 26. A Double keeps its decimal point, so 3.0.toString() is "3.0".
Double to Int drops the decimals
Converting a Double with toInt() truncates: it throws the fractional part away and does not round. 9.99.toInt() is 9, and (-9.99).toInt() is -9, so it moves toward zero. For rounding to the nearest whole number, import kotlin.math.roundToInt and call 9.99.roundToInt(), which gives 10. A Double too large for an Int is clamped to the largest Int, 2147483647.
Text to numbers can fail
Parsing a string with toInt() works only if the text is a whole number in its entirety. These cases throw a NumberFormatException and stop the program:
"12a".toInt() // fails
"3.7".toInt() // fails: the text has a decimal point
For input you don't control, use toIntOrNull() or toDoubleOrNull(). They return null instead of failing:
println("42".toIntOrNull()) // 42
println("abc".toIntOrNull()) // null
println("3.7".toDouble()) // 3.7
Int to Double, and other number types
The reverse direction is just as explicit. 25.toDouble() gives 25.0. This is the usual cure for unwanted integer division: 7 / 2 is 3 because both sides are Int, but 7.toDouble() / 2 is 3.5. The same pattern covers the other numeric types, such as toLong(). Narrowing can lose information: 3_000_000_000L.toInt() doesn't fit in 32 bits and gives -1294967296. Widening, like Int to Long or Double, is safe.
Why convert at all
The usual reason is that data arrives as text but you need to calculate with it, or the reverse. Digits stored in strings are joined, not added:
val a = "20"
val b = "22"
println(a + b) // 2022
println(a.toInt() + b.toInt()) // 42
Going the other way, you don't always need toString(). Inside a string, a template converts for you: "Age: $age" produces Age: 25, and "Age: " + age works too, since a string on the left accepts any value. Reach for toString() when you need the text on its own, for example to count its digits with age.toString().length, which is 2.
The exercise
The starter code has three values:
val age = 25
val price = 9.99
val year = "2024"
Under them, you add three new values, each converted from one of those:
ageStr, aStringmade fromagewithtoString().priceInt, anIntmade frompricewithtoInt(). The decimals are dropped, so the value is9, not 10.yearInt, anIntmade fromyearwithtoInt(). The text"2024"is entirely digits, so parsing succeeds and gives the number 2024.
val age = 25
val price = 9.99
val year = "2024"
val ageStr = age.toString()
val priceInt = price.toInt()
val yearInt = year.toInt()
Each new name is declared with val, because it is assigned once. The original variables are untouched. Afterwards ageStr + 1 is "251", yearInt + 1 is 2025 and priceInt is 9.
Common mistakes
- Expecting rounding.
toInt()on aDoubletruncates. UseroundToInt()when you want the nearest whole number. - Converting the wrong variable. Check which variable each conversion starts from.
price.toInt()andyear.toInt()are different operations: truncating versus parsing. - Parsing text that isn't a number. Spaces, letters and decimal points all break
toInt(). - Forgetting the parentheses.
age.toStringwithout()refers to a function, not its result, and doesn't compile as a value. - Converting and ignoring the result.
age.toString()alone changes nothing. Store the result or use it in an expression.
Key takeaways
- Kotlin converts between types only when you ask, with functions such as
toString(),toInt()andtoDouble(). - Double to Int truncates toward zero.
- String to number parsing fails with an exception on bad text;
toIntOrNull()returnsnullinstead. - Conversions return new values and never change the original variable.