🏗️ Class và Object
🎯 Mục tiêu: Hiểu khái niệm OOP cơ bản, cách định nghĩa class, tạo object, và các tính năng đặc biệt của Kotlin như primary constructor.
💡 Lập trình hướng đối tượng (OOP) là gì?
Phần tiêu đề “💡 Lập trình hướng đối tượng (OOP) là gì?”Hãy tưởng tượng bạn đang xây một ngôi nhà:
- Class = Bản thiết kế nhà (blueprint)
- Object = Ngôi nhà thực tế được xây từ bản thiết kế
Một bản thiết kế có thể xây nhiều ngôi nhà giống nhau, tương tự một class có thể tạo nhiều objects.
// Class = Bản thiết kếclass Person(val name: String, var age: Int)
fun main() { // Objects = Các thực thể được tạo từ class val alice = Person("Alice", 25) val bob = Person("Bob", 30)
println(alice.name) // Alice println(bob.name) // Bob}4 nguyên lý OOP cơ bản
Phần tiêu đề “4 nguyên lý OOP cơ bản”| Nguyên lý | Mô tả | Ví dụ |
|---|---|---|
| Encapsulation | Đóng gói dữ liệu và phương thức | private var password |
| Inheritance | Kế thừa từ class cha | class Dog : Animal() |
| Polymorphism | Đa hình - nhiều hình thức | Override methods |
| Abstraction | Trừu tượng hóa | Interface, abstract class |
📝 Định nghĩa Class
Phần tiêu đề “📝 Định nghĩa Class”Class đơn giản
Phần tiêu đề “Class đơn giản”// Class rỗngclass Empty
// Class với propertiesclass User { var name: String = "" var email: String = ""}
fun main() { val user = User() user.name = "Alice"}Primary Constructor - Cách Kotlin
Phần tiêu đề “Primary Constructor - Cách Kotlin”Kotlin có syntax ngắn gọn và mạnh mẽ:
// Primary constructor với val/var tự động tạo propertiesclass User(val name: String, var email: String)
// Tương đương với Java (20+ dòng code!):// class User {// private final String name;// private String email;// public User(String name, String email) {// this.name = name;// this.email = email;// }// public String getName() { return name; }// public String getEmail() { return email; }// public void setEmail(String email) { this.email = email; }// }init block - Khởi tạo logic
Phần tiêu đề “init block - Khởi tạo logic”class User(val name: String, val email: String) {
// init block chạy ngay sau khi object được tạo init { println("Creating user: $name") require(name.isNotBlank()) { "Name cannot be blank" } require(email.contains("@")) { "Invalid email format" } }
// Có thể có nhiều init blocks init { println("User validated successfully!") }}
fun main() { // Output: // Creating user: Alice // User validated successfully!}🔧 Secondary Constructor
Phần tiêu đề “🔧 Secondary Constructor”Khi cần nhiều cách để tạo object:
class User(val name: String, val email: String) {
// Secondary constructor phải gọi primary constructor constructor(name: String) : this(name, "$name@example.com") { println("Created with default email") }
constructor() : this("Guest") { println("Created as guest") }}
fun main() { val u2 = User("Bob") // Secondary 1 val u3 = User() // Secondary 2
println(u3.name) // Guest}📦 Properties
Phần tiêu đề “📦 Properties”Computed Properties (Getter tùy chỉnh)
Phần tiêu đề “Computed Properties (Getter tùy chỉnh)”class Rectangle(val width: Int, val height: Int) {
// Computed property - tính toán mỗi lần truy cập val area: Int get() = width * height
val perimeter: Int get() = 2 * (width + height)
val isSquare: Boolean get() = width == height}
fun main() { val rect = Rectangle(10, 5) println("Area: ${rect.area}") // 50 println("Perimeter: ${rect.perimeter}") // 30 println("Is square: ${rect.isSquare}") // false}Custom Setter
Phần tiêu đề “Custom Setter”class User(val id: Int) {
var name: String = "" set(value) { // 'field' là backing field field = value.trim().replaceFirstChar { it.uppercase() } }
var email: String = "" set(value) { require(value.contains("@")) { "Invalid email" } field = value.lowercase() }}
fun main() { val user = User(1) user.name = " alice "
println(user.name) // Alice}Private Setter
Phần tiêu đề “Private Setter”class Counter { var count: Int = 0 private set // Chỉ class này có thể thay đổi
fun increment() { count++ }}
fun main() { val counter = Counter() counter.increment() println(counter.count) // 1 // counter.count = 100 // ❌ Lỗi! Không thể set từ bên ngoài}Late-initialized Properties
Phần tiêu đề “Late-initialized Properties”class UserProfile { // Sẽ được khởi tạo sau (ví dụ: sau khi load từ API) lateinit var username: String
fun loadFromApi() { // Giả sử load từ API username = "loaded_user" }
fun displayUsername() { if (::username.isInitialized) { println("Username: $username") } else { println("Username not loaded yet") } }}🔒 Visibility Modifiers
Phần tiêu đề “🔒 Visibility Modifiers”| Modifier | Trong class | Top-level |
|---|---|---|
public (mặc định) |
Visible everywhere | Visible everywhere |
private |
Chỉ trong class | Chỉ trong file |
protected |
Class + subclasses | N/A (không dùng) |
internal |
Module hiện tại | Module hiện tại |
class BankAccount(private val accountNumber: String) {
private var encryptedPassword: String = "" // Chỉ trong class
var balance: Double = 0.0 private set // Public get, private set
internal fun auditLog() { // Trong module println("Audit: $accountNumber") }
protected open fun calculateInterest(): Double { // Subclasses return balance * 0.05 }}🔑 Methods (Hàm trong Class)
Phần tiêu đề “🔑 Methods (Hàm trong Class)”class Calculator { private var history = mutableListOf<String>()
fun add(a: Int, b: Int): Int { val result = a + b history.add("$a + $b = $result") return result }
fun subtract(a: Int, b: Int): Int { val result = a - b history.add("$a - $b = $result") return result }
fun printHistory() { println("=== Lịch sử tính toán ===") history.forEach { println(it) } }}
fun main() { val calc = Calculator() calc.add(10, 5) // 15 calc.subtract(10, 3) // 7 calc.printHistory() // === Lịch sử tính toán === // 10 + 5 = 15 // 10 - 3 = 7}🛠️ Thực hành
Phần tiêu đề “🛠️ Thực hành”Bài tập 1: Tạo class Student
// TODO: Tạo class Student với:// - id (readonly)// - name (readonly)// - scores (mutable list)// - averageScore (computed property)// - addScore(score: Double)// - getGrade(): String (A/B/C/D/F)Lời giải:
class Student(val id: String, val name: String) { private val scores = mutableListOf<Double>()
val averageScore: Double get() = if (scores.isEmpty()) 0.0 else scores.average()
fun addScore(score: Double) { require(score in 0.0..10.0) { "Score must be 0-10" } scores.add(score) }
fun getGrade(): String = when { averageScore >= 9.0 -> "A" averageScore >= 8.0 -> "B" averageScore >= 7.0 -> "C" averageScore >= 5.0 -> "D" else -> "F" }
override fun toString(): String { return "Student($name, avg=${"%.2f".format(averageScore)}, grade=${getGrade()})" }}
fun main() { val student = Student("SV001", "Nguyễn Văn A") student.addScore(8.5) student.addScore(9.0) student.addScore(7.5)
println(student) // Student(Nguyễn Văn A, avg=8.33, grade=B)}Bài tập 2: Tạo class BankAccount với validation
// TODO: Tạo class BankAccount với:// - accountNumber (readonly)// - ownerName (readonly)// - balance (private set, khởi tạo = 0)// - deposit(amount): validate amount > 0// - withdraw(amount): Boolean - trả về true nếu thành công// - transfer(to: BankAccount, amount): BooleanLời giải:
class BankAccount( val accountNumber: String, val ownerName: String) { var balance: Double = 0.0 private set
fun deposit(amount: Double) { require(amount > 0) { "Amount must be positive" } balance += amount println("Deposited $${"%.2f".format(amount)}. New balance: $${"%.2f".format(balance)}") }
fun withdraw(amount: Double): Boolean { require(amount > 0) { "Amount must be positive" } return if (amount <= balance) { balance -= amount println("Withdrew $${"%.2f".format(amount)}. New balance: $${"%.2f".format(balance)}") true } else { println("Insufficient funds! Balance: $${"%.2f".format(balance)}") false } }
fun transfer(to: BankAccount, amount: Double): Boolean { if (withdraw(amount)) { to.deposit(amount) println("Transferred $${"%.2f".format(amount)} to ${to.ownerName}") return true } return false }}
fun main() { val alice = BankAccount("001", "Alice") val bob = BankAccount("002", "Bob")
alice.deposit(1000.0) alice.transfer(bob, 300.0)
println("\nFinal balances:") println("Alice: $${alice.balance}") // 700.0 println("Bob: $${bob.balance}") // 300.0}📱 Trong Android
Phần tiêu đề “📱 Trong Android”// Model class cho Userclass User( val id: Long, val name: String, val email: String, var isActive: Boolean = true) { val displayName: String get() = if (name.isNotBlank()) name else "User #$id"}
// ViewModel với stateclass UserViewModel : ViewModel() { private val _users = MutableLiveData<List<User>>() val users: LiveData<List<User>> = _users
fun loadUsers() { viewModelScope.launch { val result = repository.getUsers() _users.value = result } }}
// ViewBinding với custom classclass MainActivity : AppCompatActivity() { private lateinit var binding: ActivityMainBinding private lateinit var viewModel: UserViewModel
override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) binding = ActivityMainBinding.inflate(layoutInflater) setContentView(binding.root)
viewModel = ViewModelProvider(this)[UserViewModel::class.java] observeUsers() }
private fun observeUsers() { viewModel.users.observe(this) { users -> binding.userCount.text = "Users: ${users.size}" } }}⚠️ Lưu ý quan trọng
Phần tiêu đề “⚠️ Lưu ý quan trọ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 4 nguyên lý OOP: Encapsulation, Inheritance, Polymorphism, Abstraction
- Phân biệt Class (blueprint) và Object (instance)
- Tạo class với primary constructor
- Sử dụng
val(readonly) vàvar(mutable) cho properties - Sử dụng init block để khởi tạo logic
- Tạo computed properties (custom getter)
- Sử dụng custom setter và private setter
- Hiểu visibility modifiers: public, private, protected, internal
- Biết khi nào dùng
lateinit
Tiếp theo: Property Accessors