Kotlin type conversion differs significantly from Java’s approach to type casting. While Java allows implicit widening conversions (like converting int
to long
), Kotlin requires explicit conversion functions for all numeric type transformations. This design choice prioritizes type safety and helps developers avoid unexpected behavior in their applications.
Kotlin’s explicit type conversion approach prevents common programming errors that can occur with automatic type promotion. When you attempt to assign a smaller numeric type to a larger one without explicit conversion, Kotlin will generate a compile-time error rather than silently performing the conversion.
val smallNumber: Int = 100
val largeNumber: Long = smallNumber // Compile-time error!
This strict approach ensures that type conversions are intentional and visible in your code, making it easier to track data transformations and debug type-related issues.
Kotlin provides a comprehensive set of conversion functions for transforming between different numeric types. Each conversion function follows a consistent naming pattern: to
followed by the target type name.
Function | Description | Example Usage |
---|---|---|
toByte() | Converts to Byte type | myInt.toByte() |
toShort() | Converts to Short type | myInt.toShort() |
toInt() | Converts to Int type | myLong.toInt() |
toLong() | Converts to Long type | myInt.toLong() |
toFloat() | Converts to Float type | myInt.toFloat() |
toDouble() | Converts to Double type | myFloat.toDouble() |
toChar() | Converts to Char type | myInt.toChar() |
Here are practical examples of converting between different numeric types:
// Converting Int to other numeric types
val originalInt: Int = 42
val convertedByte: Byte = originalInt.toByte()
val convertedShort: Short = originalInt.toShort()
val convertedLong: Long = originalInt.toLong()
val convertedFloat: Float = originalInt.toFloat()
val convertedDouble: Double = originalInt.toDouble()
println("Original Int: $originalInt")
println("Converted to Byte: $convertedByte")
println("Converted to Long: $convertedLong")
println("Converted to Double: $convertedDouble")
Character conversion in Kotlin follows ASCII values, making it useful for various text processing scenarios:
// Converting between Char and Int
val letterCode: Int = 65
val letter: Char = letterCode.toChar()
println("ASCII $letterCode represents: $letter") // Output: A
val characterValue: Char = 'Z'
val asciiValue: Int = characterValue.code // Note: .toInt() is deprecated
println("Character '$characterValue' has ASCII value: $asciiValue") // Output: 90
Converting strings to numeric types is a common requirement in Android development and general Kotlin programming. Kotlin provides safe conversion methods that handle potential parsing errors gracefully.
// Safe string to number conversions
val userInput = "12345"
val invalidInput = "abc123"
// Using safe conversion methods (returns null on failure)
val safeInt: Int? = userInput.toIntOrNull()
val safeDouble: Double? = userInput.toDoubleOrNull()
val failedConversion: Int? = invalidInput.toIntOrNull()
println("Safe Int conversion: $safeInt") // Output: 12345
println("Failed conversion: $failedConversion") // Output: null
// Using direct conversion (throws exception on failure)
try {
val directInt: Int = userInput.toInt()
println("Direct conversion: $directInt")
} catch (e: NumberFormatException) {
println("Conversion failed: ${e.message}")
}
Kotlin supports number parsing with different bases (radix), which is particularly useful for hexadecimal or binary conversions:
// Converting strings with different bases
val hexString = "FF"
val binaryString = "1010"
val octalString = "77"
val hexValue: Int = hexString.toInt(16) // Base 16
val binaryValue: Int = binaryString.toInt(2) // Base 2
val octalValue: Int = octalString.toInt(8) // Base 8
println("Hex FF to decimal: $hexValue") // Output: 255
println("Binary 1010 to decimal: $binaryValue") // Output: 10
println("Octal 77 to decimal: $octalValue") // Output: 63
Beyond numeric conversions, Kotlin provides powerful type casting operators for working with object hierarchies and nullable types.
The as
operator performs explicit type casting but throws a ClassCastException
if the cast fails:
fun demonstrateUnsafeCasting() {
val anyValue: Any = "Hello, Kotlin!"
// Successful cast
val stringValue: String = anyValue as String
println("Cast successful: $stringValue")
// This would throw an exception
try {
val intValue: Int = anyValue as Int // ClassCastException!
} catch (e: ClassCastException) {
println("Cast failed: ${e.message}")
}
}
The as?
operator provides a safe alternative that returns null
instead of throwing an exception:
fun demonstrateSafeCasting() {
val mixedValues: List<Any> = listOf("Kotlin", 42, 3.14, true)
for (value in mixedValues) {
val stringValue: String? = value as? String
val intValue: Int? = value as? Int
val doubleValue: Double? = value as? Double
val booleanValue: Boolean? = value as? Boolean
when {
stringValue != null -> println("Found string: $stringValue")
intValue != null -> println("Found integer: $intValue")
doubleValue != null -> println("Found double: $doubleValue")
booleanValue != null -> println("Found boolean: $booleanValue")
}
}
}
Smart casting is one of Kotlin’s most powerful features, automatically casting variables after successful type checks using the is
operator.
fun demonstrateSmartCasting(input: Any) {
if (input is String) {
// Smart cast: input is automatically treated as String
println("String length: ${input.length}")
println("Uppercase: ${input.uppercase()}")
} else if (input is Int) {
// Smart cast: input is automatically treated as Int
println("Integer value: $input")
println("Squared: ${input * input}")
} else if (input is List<*>) {
// Smart cast: input is automatically treated as List
println("List size: ${input.size}")
println("List contents: $input")
}
}
Smart casting works seamlessly with when
expressions, making code more concise and readable:
fun processDataType(data: Any): String {
return when (data) {
is String -> "Text with ${data.length} characters: $data"
is Int -> "Integer value: $data (binary: ${data.toString(2)})"
is Double -> "Decimal value: $data (rounded: ${data.toInt()})"
is List<*> -> "Collection with ${data.size} elements"
is Boolean -> "Boolean value: ${if (data) "TRUE" else "FALSE"}"
else -> "Unknown type: ${data::class.simpleName}"
}
}
Smart casting also works with logical operators, providing more sophisticated type checking:
fun advancedSmartCasting(value: Any?) {
// Smart casting with null checks
if (value != null && value is String) {
println("Non-null string: ${value.uppercase()}")
}
// Smart casting with logical OR
if (value is Int || value is Long) {
// Common supertype is Number
val numericValue = value as Number
println("Numeric value: ${numericValue.toDouble()}")
}
// Smart casting with negation
if (value !is String) {
println("Not a string type")
} else {
// Smart cast to String in else block
println("String value: $value")
}
}
Nullable type conversion requires special consideration to handle null values safely.
fun handleNullableConversions() {
val nullableString: String? = "123"
val nullValue: String? = null
// Safe conversion with null checks
val intFromNullable: Int? = nullableString?.toIntOrNull()
val intFromNull: Int? = nullValue?.toIntOrNull()
println("Converted from nullable: $intFromNullable") // Output: 123
println("Converted from null: $intFromNull") // Output: null
// Using Elvis operator for default values
val safeInt: Int = nullableString?.toIntOrNull() ?: 0
println("Safe conversion with default: $safeInt")
}
fun nullableTypeCasting() {
val nullableAny: Any? = "Kotlin Programming"
// Safe casting with nullable types
val castedString: String? = nullableAny as? String
val castedInt: Int? = nullableAny as? Int
println("Casted to String: $castedString") // Output: Kotlin Programming
println("Casted to Int: $castedInt") // Output: null
// Chaining with null-safe operations
val result = nullableAny as? String ?: "Default Value"
println("Final result: $result")
}
Here are real-world examples of type conversion in Android development contexts:
import org.json.JSONObject
fun processApiResponse(jsonString: String) {
try {
val jsonObject = JSONObject(jsonString)
// Safe type conversions for API data
val userId: Int = jsonObject.optString("user_id").toIntOrNull() ?: -1
val score: Double = jsonObject.optString("score").toDoubleOrNull() ?: 0.0
val isActive: Boolean = jsonObject.optString("is_active").toBooleanStrictOrNull() ?: false
val timestamp: Long = jsonObject.optString("timestamp").toLongOrNull() ?: System.currentTimeMillis()
println("User ID: $userId")
println("Score: $score")
println("Active: $isActive")
println("Timestamp: $timestamp")
} catch (e: Exception) {
println("JSON parsing error: ${e.message}")
}
}
import android.content.SharedPreferences
class PreferencesManager(private val prefs: SharedPreferences) {
fun saveUserSettings(userId: Int, score: Float, isEnabled: Boolean) {
prefs.edit().apply {
putString("user_id", userId.toString())
putString("user_score", score.toString())
putString("feature_enabled", isEnabled.toString())
apply()
}
}
fun getUserId(): Int {
return prefs.getString("user_id", "0")?.toIntOrNull() ?: 0
}
fun getUserScore(): Float {
return prefs.getString("user_score", "0.0")?.toFloatOrNull() ?: 0.0f
}
fun isFeatureEnabled(): Boolean {
return prefs.getString("feature_enabled", "false")?.toBooleanStrictOrNull() ?: false
}
}
fun convertDatabaseResults(cursor: android.database.Cursor): List<UserProfile> {
val profiles = mutableListOf<UserProfile>()
while (cursor.moveToNext()) {
val id = cursor.getString("id").toIntOrNull() ?: 0
val name = cursor.getString("name") ?: "Unknown"
val age = cursor.getString("age").toIntOrNull() ?: 0
val salary = cursor.getString("salary").toDoubleOrNull() ?: 0.0
val isVerified = cursor.getString("verified").toBooleanStrictOrNull() ?: false
profiles.add(UserProfile(id, name, age, salary, isVerified))
}
return profiles
}
data class UserProfile(
val id: Int,
val name: String,
val age: Int,
val salary: Double,
val isVerified: Boolean
)
Here’s a comprehensive example demonstrating various type conversion scenarios in a single application:
fun main() {
println("=== Kotlin Type Conversion Demo ===\n")
// Numeric conversions
demonstrateNumericConversions()
// String conversions
demonstrateStringConversions()
// Object casting
demonstrateObjectCasting()
// Smart casting
demonstrateSmartCasting()
// Real-world scenario
demonstrateRealWorldScenario()
}
fun demonstrateNumericConversions() {
println("1. Numeric Type Conversions:")
val originalInt = 1000
val originalDouble = 99.99
println("Original Int: $originalInt")
println("To Long: ${originalInt.toLong()}")
println("To Float: ${originalInt.toFloat()}")
println("To Byte: ${originalInt.toByte()}") // Note: May truncate
println("\nOriginal Double: $originalDouble")
println("To Int: ${originalDouble.toInt()}")
println("To Long: ${originalDouble.toLong()}")
println()
}
fun demonstrateStringConversions() {
println("2. String to Number Conversions:")
val validNumber = "12345"
val invalidNumber = "abc123"
val hexNumber = "1A"
println("Valid string '$validNumber' to Int: ${validNumber.toIntOrNull()}")
println("Invalid string '$invalidNumber' to Int: ${invalidNumber.toIntOrNull()}")
println("Hex string '$hexNumber' to Int: ${hexNumber.toIntOrNull(16)}")
// Boolean conversions
val booleanStrings = listOf("true", "false", "TRUE", "invalid")
booleanStrings.forEach { str ->
println("String '$str' to Boolean: ${str.toBooleanStrictOrNull()}")
}
println()
}
fun demonstrateObjectCasting() {
println("3. Object Type Casting:")
val mixedList: List<Any> = listOf("Kotlin", 42, 3.14159, true, null)
mixedList.forEachIndexed { index, item ->
println("Item $index:")
// Safe casting examples
when (val safeString = item as? String) {
null -> print(" Not a string")
else -> print(" String: '$safeString'")
}
when (val safeNumber = item as? Number) {
null -> print(", Not a number")
else -> print(", Number: $safeNumber")
}
when (val safeBoolean = item as? Boolean) {
null -> println(", Not a boolean")
else -> println(", Boolean: $safeBoolean")
}
}
println()
}
fun demonstrateSmartCasting() {
println("4. Smart Casting Examples:")
val testValues: List<Any> = listOf(
"Hello World",
42,
listOf(1, 2, 3),
mapOf("key" to "value")
)
testValues.forEach { value ->
val description = when (value) {
is String -> "String with ${value.length} characters"
is Int -> "Integer: $value (hex: ${value.toString(16)})"
is List<*> -> "List with ${value.size} elements: $value"
is Map<*, *> -> "Map with ${value.size} entries: $value"
else -> "Unknown type: ${value::class.simpleName}"
}
println(" $description")
}
println()
}
fun demonstrateRealWorldScenario() {
println("5. Real-World Scenario - User Input Processing:")
// Simulating user input from a form
val userInputs = mapOf(
"age" to "25",
"salary" to "75000.50",
"isStudent" to "false",
"hexColor" to "FF5733",
"invalidNumber" to "not-a-number"
)
// Process each input with appropriate type conversion
val age = userInputs["age"]?.toIntOrNull() ?: 0
val salary = userInputs["salary"]?.toDoubleOrNull() ?: 0.0
val isStudent = userInputs["isStudent"]?.toBooleanStrictOrNull() ?: false
val colorValue = userInputs["hexColor"]?.toIntOrNull(16) ?: 0
val invalidAttempt = userInputs["invalidNumber"]?.toIntOrNull()
println(" Processed User Data:")
println(" Age: $age years")
println(" Salary: $${String.format("%.2f", salary)}")
println(" Student Status: $isStudent")
println(" Color Value: #${userInputs["hexColor"]} = $colorValue (decimal)")
println(" Invalid Conversion: $invalidAttempt")
// Validation example
when {
age < 0 -> println(" ⚠️ Invalid age")
age < 18 -> println(" ✅ Minor")
age >= 18 -> println(" ✅ Adult")
}
}
When you run the complete example, you’ll see:
=== Kotlin Type Conversion Demo ===
1. Numeric Type Conversions:
Original Int: 1000
To Long: 1000
To Float: 1000.0
To Byte: -24
Original Double: 99.99
To Int: 99
To Long: 99
2. String to Number Conversions:
Valid string '12345' to Int: 12345
Invalid string 'abc123' to Int: null
Hex string '1A' to Int: 26
String 'true' to Boolean: true
String 'false' to Boolean: false
String 'TRUE' to Boolean: true
String 'invalid' to Boolean: null
3. Object Type Casting:
Item 0:
String: 'Kotlin', Not a number, Not a boolean
Item 1:
Not a string, Number: 42, Not a boolean
Item 2:
Not a string, Number: 3.14159, Not a boolean
Item 3:
Not a string, Not a number, Boolean: true
Item 4:
Not a string, Not a number, Not a boolean
4. Smart Casting Examples:
String with 11 characters
Integer: 42 (hex: 2a)
List with 3 elements: [1, 2, 3]
Map with 1 entries: {key=value}
5. Real-World Scenario - User Input Processing:
Processed User Data:
Age: 25 years
Salary: $75000.50
Student Status: false
Color Value: #FF5733 = 16733235 (decimal)
Invalid Conversion: null
✅ Adult
Understanding Kotlin type conversion is crucial for effective Android development and general Kotlin programming. Remember these essential points:
toIntOrNull()
) prevent runtime exceptionsis
checksas?
) returns null instead of throwing exceptionsMastering these Kotlin type casting techniques will help you write more robust, type-safe applications while avoiding common pitfalls in data type transformations. Whether you’re processing user input, handling API responses, or managing database operations, proper type conversion ensures your Kotlin applications run smoothly and handle edge cases gracefully.