Kotlin Type Conversion

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.

Why Kotlin Doesn’t Support Implicit Type Conversion

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.

Explicit Type Conversion Functions

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.

Complete List of Kotlin Type Conversion Functions

FunctionDescriptionExample Usage
toByte()Converts to Byte typemyInt.toByte()
toShort()Converts to Short typemyInt.toShort()
toInt()Converts to Int typemyLong.toInt()
toLong()Converts to Long typemyInt.toLong()
toFloat()Converts to Float typemyInt.toFloat()
toDouble()Converts to Double typemyFloat.toDouble()
toChar()Converts to Char typemyInt.toChar()

Numeric Type Conversion Examples

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")  

Working with Character Conversions

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  

String to Numeric Type Conversion

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 Conversion Methods

// 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}")  
}  

Advanced String Conversion with Radix

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  

Type Casting with ‘as’ and ‘as?’ Operators

Beyond numeric conversions, Kotlin provides powerful type casting operators for working with object hierarchies and nullable types.

Unsafe Cast Operator (as)

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}")  
    }  
}  

Safe Cast Operator (as?)

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 with ‘is’ Operator

Smart casting is one of Kotlin’s most powerful features, automatically casting variables after successful type checks using the is operator.

Basic Smart Casting

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 in When Expressions

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}"  
    }  
}  

Advanced Smart Casting with Logical Operators

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")  
    }  
}  

Working with Nullable Types

Nullable type conversion requires special consideration to handle null values safely.

Converting Nullable Types

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")  
}  

Nullable Type Casting

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")  
}  

Practical Android Development Examples

Here are real-world examples of type conversion in Android development contexts:

JSON Data Processing

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}")  
    }  
}  

SharedPreferences Type Handling

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  
    }  
}  

Database Type Conversion

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  
)  

Complete Working Example

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")  
    }  
}  

Output

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  

Key Takeaways

Understanding Kotlin type conversion is crucial for effective Android development and general Kotlin programming. Remember these essential points:

  • Explicit conversion is required for all numeric type transformations
  • Safe conversion methods (like toIntOrNull()) prevent runtime exceptions
  • Smart casting automatically handles type conversions after successful is checks
  • Safe cast operator (as?) returns null instead of throwing exceptions
  • String conversions support different number bases (radix) for specialized parsing

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