Type Checks and Casts (Kiểm tra và Ép kiểu)
🎯 Mục tiêu: Nắm vững cách kiểm tra kiểu (
is), ép kiểu an toàn (as?) và cơ chế Smart Casts thông minh của Kotlin.
🕵️ Kiểm tra kiểu (is / !is)
Dùng is để kiểm tra xem một object có phải là kiểu cụ thể hay không (tương đương instanceof trong Java).
if (obj is String) {
print(obj.length)
}
if (obj !is String) { // Tương đương !(obj is String)
print("Not a String")
}🧠 Smart Casts
Kotlin compiler cực kỳ thông minh! Nếu bạn đã kiểm tra kiểu với is, nó sẽ tự động ép kiểu (cast) biến đó trong phạm vi tương ứng mà không cần bạn làm gì thêm.
Với if và when
fun demo(x: Any) {
if (x is String) {
print(x.length) // x tự động là String
}
}
when (x) {
is Int -> print(x + 1)
is String -> print(x.length + 1)
is IntArray -> print(x.sum())
}Với && và ||
// Bên phải &&
if (x is String && x.length > 0) {
print(x.length) // x là String
}
// Bên phải || (negative check)
if (x !is String || x.isEmpty()) {
return
}Smart Casts hoạt động khi nào?
Smart cast chỉ hoạt động khi compiler đảm bảo biến không thay đổi giữa lúc kiểm tra và lúc sử dụng.
- val local variables: Luôn hoạt động (trừ local delegated properties).
- val properties: Hoạt động nếu private/internal hoặc check trong cùng module.
- var local variables: Hoạt động nếu không bị thay đổi giữa check và use, không bị capture bởi lambda thay đổi nó.
- var properties: Không bao giờ hoạt động (vì code khác có thể thay đổi bất cứ lúc nào).
🔄 Ép kiểu (Type Casts)
Unsafe Cast (as)
Dùng as khi bạn chắc chắn về kiểu. Nếu sai, nó sẽ ném ClassCastException.
val y = null
val x: String = y as String // ❌ Exception: null cannot be cast to non-null StringĐể cast nullable:
val x: String? = y as String? // ✅ OK, x = nullSafe Cast (as?)
Dùng as? để tránh exception. Nếu cast thất bại, nó trả về null.
val x: String? = y as? StringĐây là cách được khuyên dùng để xử lý ép kiểu an toàn.
🧬 Generics Type Checks
Do Type Erasure (xóa kiểu generic tại runtime), bạn không thể kiểm tra cụ thể generic type của một instance (trừ khi dùng inline reified).
// ❌ Lỗi biên dịch
// if (list is List<String>) { ... }
// ✅ OK: Kiểm tra Star-projected type
if (list is List<*>) {
// list là một List nào đó, nhưng không biết phần tử là gì
}Để kiểm tra generic type thực sự, dùng Inline Functions với reified:
inline fun <reified T> isType(value: Any): Boolean {
return value is T
}🛠️ Thực hành
Bài tập 1: Xử lý Any
fun process(input: Any) {
// TODO:
// Nếu input là String, in độ dài
// Nếu input là Int, in bình phương
// Nếu input là List, in số phần tử
// Dùng when expression
}Lời giải:
fun process(input: Any) {
when (input) {
is String -> println("String length: ${input.length}")
is Int -> println("Square: ${input * input}")
is List<*> -> println("List size: ${input.size}")
else -> println("Unknown type")
}
}✅ Checklist
- Sử dụng
isđể kiểm tra kiểu - Tận dụng Smart Casts để code gọn hơn
- Luôn ưu tiên
as?thay vìas - Hiểu hạn chế của Smart Casts với
varproperties