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
| Feature | Description |
|---|---|
| Basic syntax | fun outer() { fun local() { } } |
| Access outer vars | Local functions can access outer scope |
| Closure | Captures and modifies outer variables |
| Extension | Can define local extension functions |
| Nested | Can have multiple levels of nesting |
| Tailrec | Supports 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