Chuyển đổi kiểu dữ liệu (Type Conversion) trong Kotlin
🎯 Mục tiêu: Học cách chuyển đổi kiểu dữ liệu an toàn trong Kotlin với các method conversion và smart casts.
💡 Khái niệm
Kotlin là strongly typed - không tự động chuyển đổi kiểu (không như JavaScript). Bạn phải chuyển đổi tường minh.
val intValue: Int = 100
// ❌ Lỗi - Kotlin không implicit conversion
// val longValue: Long = intValue
// ✅ Phải convert tường minh
val longValue: Long = intValue.toLong()📝 Conversion Methods cho số
Các method cơ bản
Mỗi kiểu số đều có các method chuyển đổi:
val number = 42
val toByte: Byte = number.toByte()
val toShort: Short = number.toShort()
val toInt: Int = number.toInt()
val toLong: Long = number.toLong()
val toFloat: Float = number.toFloat()
val toDouble: Double = number.toDouble()
val toChar: Char = number.toChar()Ví dụ chuyển đổi
// Int → Long
val count = 1000000
val bigCount = count.toLong()
// Double → Int (mất phần thập phân)
val price = 99.99
val roundedPrice = price.toInt() // 99
// Float ↔ Double
val floatVal = 3.14f
val doubleVal = floatVal.toDouble()🔢 Chuyển đổi String ↔ Số
String → Số
// Cách an toàn (trả về null nếu không parse được)
val str = "42"
val number = str.toIntOrNull() // 42
val invalid = "abc".toIntOrNull() // null
// Các kiểu khác
val long = "1000000".toLongOrNull()
val double = "3.14".toDoubleOrNull()
val float = "3.14".toFloatOrNull()Cách không an toàn (throw exception)
val number = "42".toInt() // 42
val error = "abc".toInt() // ❌ NumberFormatException![!TIP] Luôn dùng
toIntOrNull()khi parse input từ người dùng!
Số → String
val number = 42
val str = number.toString() // "42"
// Format số
val price = 15000000
val formatted = "%,d".format(price) // "15,000,000"
val decimal = 3.14159
val fixed = "%.2f".format(decimal) // "3.14"🎯 Smart Casts với is
Kotlin tự động cast sau khi kiểm tra kiểu với is:
fun process(value: Any) {
if (value is String) {
// Smart cast: value đã là String ở đây
println("String length: ${value.length}")
}
if (value is Int) {
// Smart cast: value đã là Int ở đây
println("Squared: ${value * value}")
}
}Smart Cast với when
fun describe(obj: Any): String = when (obj) {
is Int -> "Integer: ${obj * 2}"
is String -> "String: ${obj.uppercase()}"
is Boolean -> if (obj) "True" else "False"
is List<*> -> "List of ${obj.size} items"
else -> "Unknown type"
}Negative Smart Cast
fun processNotNull(value: Any?) {
if (value !is String) {
return // Exit early
}
// value is String from here
println(value.uppercase())
}⚠️ Unsafe Cast với as
Cast không an toàn - throw exception nếu không thành công:
val obj: Any = "Hello"
val str: String = obj as String // ✅ OK
// val num: Int = obj as Int // ❌ ClassCastException!✅ Safe Cast với as?
Cast an toàn - trả về null nếu không thành công:
val obj: Any = "Hello"
val str: String? = obj as? String // "Hello"
val num: Int? = obj as? Int // null (không crash)🔄 Type Checking
Kiểm tra kiểu với is
val value: Any = "Kotlin"
println(value is String) // true
println(value is Int) // false
println(value !is Int) // trueKiểm tra nullable với is
val nullableValue: Any? = null
println(nullableValue is String) // false
println(nullableValue is String?) // Không cần - dùng null check
println(nullableValue == null) // true📊 Generics và Type Erasure
Kotlin sử dụng type erasure - thông tin generic bị xóa khi runtime:
val list = listOf(1, 2, 3)
// ❌ Không thể kiểm tra generic type
// if (list is List<Int>) { } // Lỗi compile
// ✅ Chỉ kiểm tra được List
if (list is List<*>) {
println("It's a list")
}Reified type với inline functions
inline fun <reified T> checkType(value: Any): Boolean {
return value is T
}
println(checkType<String>("Hello")) // true
println(checkType<Int>("Hello")) // false🛠️ Thực hành
Bài tập 1: Parse user input
fun main() {
val ageInput = "25"
val priceInput = "99.99"
val invalidInput = "abc"
// TODO: Parse sang số an toàn, mặc định 0
}Lời giải:
fun main() {
val ageInput = "25"
val priceInput = "99.99"
val invalidInput = "abc"
val age = ageInput.toIntOrNull() ?: 0
val price = priceInput.toDoubleOrNull() ?: 0.0
val invalid = invalidInput.toIntOrNull() ?: 0
println("Age: $age") // 25
println("Price: $price") // 99.99
println("Invalid: $invalid") // 0
}Bài tập 2: Process different types
fun main() {
val items = listOf(42, "Hello", 3.14, true, listOf(1, 2, 3))
// TODO: Xử lý từng item theo kiểu
}Lời giải:
fun main() {
val items: List<Any> = listOf(42, "Hello", 3.14, true, listOf(1, 2, 3))
for (item in items) {
val description = when (item) {
is Int -> "Integer: ${item * 2}"
is String -> "String: ${item.uppercase()}"
is Double -> "Double: ${"%.2f".format(item)}"
is Boolean -> "Boolean: ${if (item) "Yes" else "No"}"
is List<*> -> "List with ${item.size} items"
else -> "Unknown"
}
println(description)
}
}Output:
Integer: 84
String: HELLO
Double: 3.14
Boolean: Yes
List with 3 itemsBài tập 3: Safe conversion chain
fun main() {
val config = mapOf(
"port" to "8080",
"timeout" to "30",
"debug" to "true",
"invalid" to "abc"
)
// TODO: Parse thành các type đúng
}Lời giải:
fun main() {
val config = mapOf(
"port" to "8080",
"timeout" to "30",
"debug" to "true",
"invalid" to "abc"
)
val port = config["port"]?.toIntOrNull() ?: 80
val timeout = config["timeout"]?.toIntOrNull() ?: 60
val debug = config["debug"]?.toBooleanStrictOrNull() ?: false
val invalid = config["invalid"]?.toIntOrNull() ?: -1
println("Port: $port") // 8080
println("Timeout: $timeout") // 30
println("Debug: $debug") // true
println("Invalid: $invalid") // -1
}📱 Trong Android
// Parse Intent extras
val userId = intent.getStringExtra("USER_ID")?.toIntOrNull() ?: -1
// Parse JSON response
val jsonObject = JSONObject(responseString)
val count = jsonObject.optInt("count", 0)
val name = jsonObject.optString("name", "")
// RecyclerView ViewHolder
class MyViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) {
fun bind(item: Any) {
when (item) {
is HeaderItem -> bindHeader(item)
is ContentItem -> bindContent(item)
is FooterItem -> bindFooter(item)
}
}
}⚠️ Lưu ý quan trọng
[!WARNING] Cẩn thận với Float precision:
val f = 3.14f val d = f.toDouble() println(d) // 3.140000104904175 (không chính xác!) // Workaround val accurate = f.toString().toDouble() // 3.14
[!CAUTION] Overflow khi converting:
val big = 1000L val small = big.toByte() println(small) // -24 (overflow, không phải 1000!)
✅ Checklist - Tự kiểm tra
Sau bài học này, bạn có thể:
- Chuyển đổi số với
toInt(),toLong(),toDouble(), etc. - Parse String an toàn với
toIntOrNull(),toDoubleOrNull() - Sử dụng
isđể kiểm tra kiểu - Hiểu Smart Cast sau khi kiểm tra
is - Phân biệt
as(unsafe) vàas?(safe) cast - Xử lý type erasure với generics
Tiếp theo: Nhập dữ liệu từ bàn phím