Skip to Content
Kotlin📘 Ngôn ngữ KotlinLocal Functions (Hàm cục bộ)

Local Functions trong Kotlin

1. Giới thiệu

Local functions (hàm cục bộ) là các functions được định nghĩa bên trong functions khác. Chúng giúp giảm code duplication và tổ chức code tốt hơn mà không cần tạo private methods ở class level.

2. Cú pháp cơ bản

fun processUser(user: User) { // Local function fun validate(value: String, fieldName: String) { if (value.isEmpty()) { throw IllegalArgumentException("$fieldName cannot be empty") } } // Sử dụng local function validate(user.name, "Name") validate(user.email, "Email") // Process user... println("Processing ${user.name}") } data class User(val name: String, val email: String) fun main() { processUser(User("Alice", "alice@example.com")) // processUser(User("", "test@test.com")) // Throws exception }

3. Access Outer Function Variables

Local functions có thể truy cập variables và parameters của outer function:

fun saveUser(user: User) { // Local function access outer parameter 'user' fun validateField(value: String, fieldName: String) { if (value.isEmpty()) { throw IllegalArgumentException( "Cannot save user ${user.id}: $fieldName is empty" ) } } validateField(user.name, "Name") validateField(user.address, "Address") // Save to database... } data class User(val id: Int, val name: String, val address: String)

Closure - capture variables

fun createCounter(): () -> Int { var count = 0 // Local function captures 'count' fun increment(): Int { count++ return count } return ::increment } fun main() { val counter = createCounter() println(counter()) // 1 println(counter()) // 2 println(counter()) // 3 }

4. Nested Local Functions

Có thể nest nhiều levels:

fun processData(data: List<String>) { fun processItem(item: String) { fun validate(s: String) { require(s.isNotBlank()) { "Item cannot be blank" } } fun transform(s: String): String { return s.trim().uppercase() } validate(item) val result = transform(item) println("Processed: $result") } data.forEach { processItem(it) } } fun main() { processData(listOf("hello", " world ", "kotlin")) // Processed: HELLO // Processed: WORLD // Processed: KOTLIN }

5. Extension Functions as Local Functions

fun processNumbers(numbers: List<Int>) { // Local extension function fun Int.isEvenAndPositive(): Boolean { return this > 0 && this % 2 == 0 } fun Int.format(): String { return "Number: $this (${if (isEvenAndPositive()) "valid" else "invalid"})" } numbers.forEach { println(it.format()) } } fun main() { processNumbers(listOf(2, 3, -4, 6, 7)) // Number: 2 (valid) // Number: 3 (invalid) // Number: -4 (invalid) // Number: 6 (valid) // Number: 7 (invalid) }

6. Use Cases thực tế

Form Validation

fun validateForm(form: RegistrationForm): ValidationResult { val errors = mutableListOf<String>() fun addError(field: String, message: String) { errors.add("$field: $message") } fun validateRequired(value: String, fieldName: String) { if (value.isBlank()) { addError(fieldName, "is required") } } fun validateEmail(email: String) { if (!email.contains("@")) { addError("Email", "invalid format") } } fun validatePassword(password: String) { if (password.length < 8) { addError("Password", "must be at least 8 characters") } if (!password.any { it.isDigit() }) { addError("Password", "must contain at least one digit") } } // Validate all fields validateRequired(form.username, "Username") validateRequired(form.email, "Email") validateEmail(form.email) validatePassword(form.password) return if (errors.isEmpty()) { ValidationResult.Success } else { ValidationResult.Failure(errors) } } data class RegistrationForm( val username: String, val email: String, val password: String ) sealed class ValidationResult { object Success : ValidationResult() data class Failure(val errors: List<String>) : ValidationResult() }

Recursive algorithms

fun parseExpression(input: String): Int { var pos = 0 fun parseNumber(): Int { val start = pos while (pos < input.length && input[pos].isDigit()) { pos++ } return input.substring(start, pos).toInt() } fun parseTerm(): Int { var result = parseNumber() while (pos < input.length && input[pos] in "*/") { val op = input[pos++] val next = parseNumber() result = if (op == '*') result * next else result / next } return result } fun parseExpr(): Int { var result = parseTerm() while (pos < input.length && input[pos] in "+-") { val op = input[pos++] val next = parseTerm() result = if (op == '+') result + next else result - next } return result } return parseExpr() } fun main() { println(parseExpression("2+3*4")) // 14 println(parseExpression("10-2*3")) // 4 }

Building complex strings

fun generateReport(data: ReportData): String { val output = StringBuilder() var indentLevel = 0 fun indent() { output.append(" ".repeat(indentLevel)) } fun line(text: String) { indent() output.appendLine(text) } fun section(title: String, block: () -> Unit) { line("[$title]") indentLevel++ block() indentLevel-- } // Build report line("=== Report: ${data.title} ===") line("") section("Summary") { line("Total items: ${data.items.size}") line("Generated: ${data.timestamp}") } section("Items") { data.items.forEachIndexed { index, item -> line("${index + 1}. $item") } } return output.toString() } data class ReportData( val title: String, val items: List<String>, val timestamp: String )

7. So sánh với Private Methods

Private Method (class level)

class UserValidator { fun validate(user: User): Boolean { return validateName(user.name) && validateEmail(user.email) } private fun validateName(name: String): Boolean = name.isNotBlank() private fun validateEmail(email: String): Boolean = email.contains("@") }

Local Function (function level)

fun validateUser(user: User): Boolean { fun validateName(name: String) = name.isNotBlank() fun validateEmail(email: String) = email.contains("@") return validateName(user.name) && validateEmail(user.email) }

Khi nào dùng local function:

  • Logic chỉ dùng trong 1 function
  • Cần access outer function variables
  • Muốn organize code locally
  • Reduce class-level noise

Khi nào dùng private method:

  • Logic dùng ở nhiều methods
  • Cần testability
  • Logic độc lập, không cần outer context

8. Local Functions với Generics

fun <T> processList(items: List<T>, transform: (T) -> String) { fun formatItem(index: Int, item: T): String { return "${index + 1}. ${transform(item)}" } fun printAll() { items.forEachIndexed { index, item -> println(formatItem(index, item)) } } printAll() } fun main() { processList(listOf(1, 2, 3)) { "Number: $it" } // 1. Number: 1 // 2. Number: 2 // 3. Number: 3 }

9. Tail Recursion với Local Functions

fun factorial(n: Int): Long { tailrec fun factorialTail(n: Int, accumulator: Long): Long { return if (n <= 1) accumulator else factorialTail(n - 1, n * accumulator) } return factorialTail(n, 1) } fun fibonacci(n: Int): Long { tailrec fun fibTail(n: Int, a: Long, b: Long): Long { return when (n) { 0 -> a 1 -> b else -> fibTail(n - 1, b, a + b) } } return fibTail(n, 0, 1) } fun main() { println(factorial(10)) // 3628800 println(fibonacci(10)) // 55 }

📝 Tóm tắt

FeatureDescription
Basic syntaxfun outer() { fun local() { } }
Access outer varsLocal functions can access outer scope
ClosureCaptures and modifies outer variables
ExtensionCan define local extension functions
NestedCan have multiple levels of nesting
TailrecSupports tail recursion optimization

Best practices:

  • Dùng local functions cho helper logic chỉ dùng locally
  • Giữ local functions ngắn gọn (< 10-15 lines)
  • Tránh nested quá sâu (max 2 levels)
  • Consider private methods nếu logic phức tạp hoặc cần test
Last updated on