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 functionsLờ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