📦 Biến - val và var
🎯 Mục tiêu: Hiểu sự khác biệt giữa
valvàvar, và tại sao Kotlin khuyến khích immutability (tính bất biến).
💡 Khái niệm
Phần tiêu đề “💡 Khái niệm”Hãy tưởng tượng bạn có hai loại hộp:
| Loại hộp | Keyword | Đặc điểm |
|---|---|---|
| 🔒 Hộp khóa | val |
Sau khi bỏ đồ vào, không thể thay đổi |
| 📦 Hộp mở | var |
Có thể thay đồ bất cứ lúc nào |
Kotlin mặc định muốn bạn dùng “hộp khóa” (val) vì an toàn hơn!
val name = "Bumbii" // 🔒 Không thể thay đổivar score = 100 // 📦 Có thể thay đổi
// name = "Other" // ❌ Lỗi compile!score = 150 // ✅ OK📝 Cú pháp khai báo biến
Phần tiêu đề “📝 Cú pháp khai báo biến”val - Biến không thay đổi (Immutable reference)
Phần tiêu đề “val - Biến không thay đổi (Immutable reference)”val pi = 3.14159val appName = "Bumbii Academy"val isActive = true
// Không thể gán lại giá trị// pi = 3.14 // ❌ Compile error: Val cannot be reassignedvar - Biến có thể thay đổi (Mutable reference)
Phần tiêu đề “var - Biến có thể thay đổi (Mutable reference)”var count = 0count = 1 // ✅ OKcount = 2 // ✅ OK
var userName = "Alice"userName = "Bob" // ✅ OK🔍 Type Inference - Kotlin tự suy luận kiểu
Phần tiêu đề “🔍 Type Inference - Kotlin tự suy luận kiểu”Kotlin thông minh! Bạn không cần khai báo kiểu dữ liệu, Kotlin sẽ tự suy luận:
// Kotlin tự hiểu kiểu dữ liệuval name = "Kotlin" // Stringval age = 25 // Intval height = 1.75 // Doubleval isStudent = true // Boolean
// Hoặc khai báo tường minh (explicit)val name: String = "Kotlin"val age: Int = 25val height: Double = 1.75val isStudent: Boolean = true⚠️ val không có nghĩa là object immutable!
Phần tiêu đề “⚠️ val không có nghĩa là object immutable!”Đây là điểm hay bị hiểu nhầm:
val numbers = mutableListOf(1, 2, 3)
// Không thể gán list mới// numbers = mutableListOf(4, 5, 6) // ❌ Compile error
// NHƯNG có thể thay đổi NỘI DUNG của listnumbers.add(4) // ✅ OK - giờ là [1, 2, 3, 4]numbers[0] = 100 // ✅ OK - giờ là [100, 2, 3, 4]Nếu muốn list thực sự immutable:
val numbers = listOf(1, 2, 3) // Immutable list// numbers.add(4) // ❌ Không có method add()🔧 Late Initialization với lateinit
Phần tiêu đề “🔧 Late Initialization với lateinit”Đôi khi bạn không thể khởi tạo biến ngay lập tức:
class UserProfile { // Sẽ được khởi tạo sau (ví dụ: sau khi load từ database) lateinit var userName: String
fun initializeUser() { userName = "Bumbii" }
fun printUser() { // Kiểm tra đã khởi tạo chưa if (::userName.isInitialized) { println("User: $userName") } else { println("User chưa được khởi tạo") } }}📌 Hằng số với const val
Phần tiêu đề “📌 Hằng số với const val”Để khai báo hằng số compile-time:
// Top-level hoặc trong objectconst val PI = 3.14159const val MAX_USERS = 1000const val APP_NAME = "Bumbii Academy"
object Config { const val API_URL = "https://api.bumbii.com" const val TIMEOUT = 30}| Keyword | Khi nào dùng |
|---|---|
val |
Giá trị không đổi trong runtime |
const val |
Giá trị biết trước lúc compile (primitives + String) |
val currentTime = System.currentTimeMillis() // ✅ Runtime value// const val currentTime = System.currentTimeMillis() // ❌ Lỗi!📏 Quy tắc đặt tên biến
Phần tiêu đề “📏 Quy tắc đặt tên biến”Kotlin sử dụng camelCase:
// ✅ Đúngval firstName = "John"val totalAmount = 1000val isUserLoggedIn = true
// ✅ Hằng số dùng SCREAMING_SNAKE_CASEconst val MAX_RETRY_COUNT = 3const val DATABASE_NAME = "bumbii_db"
// ❌ Saival first_name = "John" // underscore không đúng quy ướcval TOTAL = 1000 // ALL CAPS cho biến thườngQuy tắc cơ bản:
- Bắt đầu bằng chữ cái hoặc
_ - Không bắt đầu bằng số
- Không dùng keyword của Kotlin (
val,var,class,fun…) - Phân biệt hoa/thường (
name≠Name)
🛠️ Thực hành
Phần tiêu đề “🛠️ Thực hành”Bài tập 1: Phân biệt val và var
fun main() { // TODO: Khai báo biến cho thông tin sinh viên // - Mã sinh viên (không đổi) // - Tên sinh viên (không đổi) // - Điểm trung bình (có thể thay đổi) // - Số tín chỉ đã học (có thể tăng)}Lời giải:
fun main() { val studentId = "SV001" // 🔒 Mã SV không đổi val studentName = "Nguyễn Văn A" // 🔒 Tên không đổi var gpa = 3.5 // 📦 Điểm có thể cập nhật var totalCredits = 30 // 📦 Tín chỉ tăng dần
println("=== THÔNG TIN SINH VIÊN ===") println("Mã SV: $studentId") println("Họ tên: $studentName") println("GPA: $gpa") println("Tín chỉ: $totalCredits")
// Cập nhật sau kỳ học mới gpa = 3.7 totalCredits = 45
println("\n=== SAU KỲ HỌC MỚI ===") println("GPA: $gpa") println("Tín chỉ: $totalCredits")}Bài tập 2: Sửa lỗi trong code sau
fun main() { val userName = "Alice" userName = "Bob" // ???
const val MAX = 100 // ???
lateinit val message: String // ???
lateinit var count: Int // ???}Lời giải:
// Top-level hoặc trong objectconst val MAX = 100
fun main() { var userName = "Alice" // Đổi val → var nếu cần thay đổi userName = "Bob" // ✅ OK
lateinit var message: String // lateinit chỉ dùng với var message = "Hello"
var count: Int = 0 // lateinit không dùng với primitive}📱 Trong Android / Jetpack Compose
Phần tiêu đề “📱 Trong Android / Jetpack Compose”class MainActivity : AppCompatActivity() {
// lateinit thường dùng cho Views private lateinit var binding: ActivityMainBinding
override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) binding = ActivityMainBinding.inflate(layoutInflater) setContentView(binding.root) }}Jetpack Compose:
@Composablefun Counter() { // Sử dụng remember và mutableStateOf var count by remember { mutableStateOf(0) }
Column { Text("Count: $count") Button(onClick = { count++ }) { Text("Tăng") } }}✅ Checklist - Tự kiểm tra
Phần tiêu đề “✅ Checklist - Tự kiểm tra”Sau bài học này, bạn có thể:
- Hiểu sự khác biệt giữa
val(immutable reference) vàvar(mutable reference) - Biết khi nào nên dùng
val(mặc định) và khi nào cầnvar - Hiểu rằng
valkhông đảm bảo object immutable - Sử dụng
lateinit varcho khởi tạo muộn - Phân biệt
valvàconst val - Đặt tên biến đúng quy ước camelCase
Tiếp theo: Giá trị và Kiểu dữ liệu