Bỏ qua để đến nội dung

📍 Local Functions

🎯 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 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
}

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
}

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")
}
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
}

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
}
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)
}

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)
}

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
}

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)
}

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)
}
}
}
}


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