Skip to Content
Kotlin📘 Ngôn ngữ Kotlin📍 Local Functions

Local Functions trong Kotlin

🎯 Mục tiêu: Hiểu Local Functions - hàm lồng bên trong hàm khác, giúp tổ chức code tốt hơn và tận dụng closure.


💡 Local Functions là gì?

Local function là hàm được định nghĩa bên trong hàm khác. Chúng chỉ visible trong scope của outer function.

fun outerFunction() { // Local function - chỉ visible trong outerFunction fun innerFunction() { println("I'm a local function!") } innerFunction() // Gọi local function } fun main() { outerFunction() // innerFunction() // ❌ Lỗi! Không thể gọi từ bên ngoài }

📝 Closure - Truy cập biến bên ngoài

Local functions có thể truy cập và modify biến của outer function (closure):

fun calculateTotal(prices: List<Double>, taxRate: Double): Double { var total = 0.0 var taxAmount = 0.0 // Local function access biến bên ngoài fun addPrice(price: Double) { val tax = price * taxRate total += price taxAmount += tax } for (price in prices) { addPrice(price) } println("Subtotal: $total, Tax: $taxAmount") return total + taxAmount } fun main() { val prices = listOf(100.0, 200.0, 50.0) val total = calculateTotal(prices, 0.1) println("Total: $total") // Subtotal: 350.0, Tax: 35.0 // Total: 385.0 }

🎯 Use Case 1: Validation

Tránh lặp code validation:

// ❌ Không tốt: Code lặp fun saveUser(name: String, email: String, phone: String) { if (name.isBlank()) throw IllegalArgumentException("Name cannot be blank") if (email.isBlank()) throw IllegalArgumentException("Email cannot be blank") if (phone.isBlank()) throw IllegalArgumentException("Phone cannot be blank") // Save... } // ✅ Tốt hơn: Local function fun saveUser(name: String, email: String, phone: String) { fun validate(value: String, fieldName: String) { if (value.isBlank()) { throw IllegalArgumentException("$fieldName cannot be blank") } } validate(name, "Name") validate(email, "Email") validate(phone, "Phone") // Validation passed, save user... println("Saving user: $name, $email, $phone") }

Validation nâng cao

data class User(val name: String, val email: String, val age: Int) fun registerUser(user: User) { fun validateField(value: Any?, fieldName: String, validator: (Any?) -> Boolean, message: String) { if (!validator(value)) { throw IllegalArgumentException("$fieldName: $message") } } validateField(user.name, "Name", { it is String && it.isNotBlank() }, "cannot be blank") validateField(user.email, "Email", { it is String && it.contains("@") }, "must be valid email") validateField(user.age, "Age", { it is Int && it >= 18 }, "must be at least 18") println("User registered: ${user.name}") } fun main() { registerUser(User("Alice", "[email protected]", 25)) // ✅ // registerUser(User("Bob", "invalid", 16)) // ❌ Age: must be at least 18 }

🔄 Use Case 2: Recursive Helper

Local function thường được dùng cho recursive helper:

fun findPathsInTree(root: TreeNode?): List<List<Int>> { val allPaths = mutableListOf<List<Int>>() // Recursive local function fun dfs(node: TreeNode?, currentPath: MutableList<Int>) { if (node == null) return currentPath.add(node.value) if (node.left == null && node.right == null) { // Leaf node - save path allPaths.add(currentPath.toList()) } else { dfs(node.left, currentPath) dfs(node.right, currentPath) } currentPath.removeAt(currentPath.lastIndex) // Backtrack } dfs(root, mutableListOf()) return allPaths }

Ví dụ đơn giản: Fibonacci

fun fibonacci(n: Int): Long { val cache = mutableMapOf<Int, Long>() fun fib(k: Int): Long { if (k <= 1) return k.toLong() return cache.getOrPut(k) { fib(k - 1) + fib(k - 2) } } return fib(n) } fun main() { println(fibonacci(50)) // 12586269025 (fast with memoization) }

📋 Use Case 3: Tổ chức code phức tạp

Chia nhỏ logic phức tạp mà không expose helper functions:

fun processOrder(order: Order): OrderResult { // Local functions để tổ chức code fun validateOrder(): Boolean { return order.items.isNotEmpty() && order.total > 0 } fun calculateDiscount(): Double { return when { order.total > 1000 -> order.total * 0.1 order.total > 500 -> order.total * 0.05 else -> 0.0 } } fun applyTax(amount: Double): Double { return amount * 1.1 // 10% tax } fun createReceipt(finalAmount: Double): String { return """ Order #${order.id} Items: ${order.items.size} Subtotal: $${order.total} Discount: $${calculateDiscount()} Final (with tax): $${"%.2f".format(finalAmount)} """.trimIndent() } // Main logic if (!validateOrder()) { return OrderResult.Error("Invalid order") } val discount = calculateDiscount() val afterDiscount = order.total - discount val finalAmount = applyTax(afterDiscount) val receipt = createReceipt(finalAmount) return OrderResult.Success(finalAmount, receipt) }

🔧 Local Extension Functions

Bạn cũng có thể tạo local extension functions:

fun processText(text: String): String { // Local extension function fun String.normalize(): String { return this.trim().lowercase().replace("\\s+".toRegex(), " ") } fun String.titleCase(): String { return split(" ").joinToString(" ") { word -> word.replaceFirstChar { it.uppercase() } } } return text.normalize().titleCase() } fun main() { val result = processText(" HELLO WORLD ") println(result) // Hello World }

🛠️ Thực hành

Bài tập: Refactor với local functions

// ❌ Code lặp fun printUserReport(users: List<User>) { println("=".repeat(50)) println("USER REPORT") println("=".repeat(50)) for (user in users) { println("-".repeat(30)) println("Name: ${user.name}") println("Email: ${user.email}") println("-".repeat(30)) } println("=".repeat(50)) println("Total: ${users.size} users") println("=".repeat(50)) } // TODO: Refactor dùng local functions

Lời giải:

data class User(val name: String, val email: String) fun printUserReport(users: List<User>) { fun separator(char: Char = '-', length: Int = 50) { println(char.toString().repeat(length)) } fun header(title: String) { separator('=') println(title) separator('=') } fun userSection(user: User) { separator() println("Name: ${user.name}") println("Email: ${user.email}") } fun footer(count: Int) { separator() separator('=') println("Total: $count users") separator('=') } header("USER REPORT") users.forEach { userSection(it) } footer(users.size) } fun main() { val users = listOf( User("Alice", "[email protected]"), User("Bob", "[email protected]") ) printUserReport(users) }

📱 Trong Android

class UserViewModel : ViewModel() { fun loadAndDisplayUser(userId: String) { // Local validation fun isValidId(id: String): Boolean { return id.isNotBlank() && id.length >= 5 } // Local error handler fun handleError(error: Throwable) { _error.value = when (error) { is NetworkException -> "Network error" is NotFoundException -> "User not found" else -> "Unknown error" } } // Local success handler fun handleSuccess(user: User) { _user.value = user _loading.value = false } if (!isValidId(userId)) { _error.value = "Invalid user ID" return } viewModelScope.launch { _loading.value = true try { val user = repository.getUser(userId) handleSuccess(user) } catch (e: Exception) { handleError(e) } } } }

⚠️ Lưu ý quan trọng

⚠️

Đừng lạm dụng local functions:

  • Nếu local function quá dài hoặc phức tạp → extract thành private function
  • Nếu cần reuse ở nhiều nơi → đặt ở class/file level
💡

Khi nào dùng Local Functions:

  • Tránh code lặp trong một function
  • Logic chỉ relevant cho function đó
  • Cần access biến của outer function (closure)
  • Recursive helper với memoization

✅ Checklist - Tự kiểm tra

Sau bài học này, bạn có thể:

  • Tạo local functions bên trong functions khác
  • Hiểu closure - truy cập và modify biến bên ngoài
  • Sử dụng local functions cho validation
  • Sử dụng local functions cho recursive helpers
  • Tổ chức code phức tạp với local functions
  • Tạo local extension functions

Tiếp theo: Lambda Expressions

Last updated on