λ Functional (SAM) Interfaces
🎯 Mục tiêu: Hiểu Functional Interface (SAM interface) và SAM Conversions - cách Kotlin cho phép dùng lambda thay cho interface với một method.
💡 Functional Interface là gì?
Phần tiêu đề “💡 Functional Interface là gì?”Functional Interface (hay SAM - Single Abstract Method) là interface chỉ có một phương thức abstract duy nhất.
Trong Kotlin, khai báo với fun interface:
fun interface IntPredicate { fun accept(i: Int): Boolean}Tại sao “functional”?
Phần tiêu đề “Tại sao “functional”?”Vì nó đại diện cho một chức năng (function) duy nhất, nên có thể thay thế bằng lambda:
// Cách truyền thống - verboseval isEven = object : IntPredicate { override fun accept(i: Int): Boolean { return i % 2 == 0 }}
// SAM Conversion - concise!val isEvenLambda = IntPredicate { it % 2 == 0 }
fun main() { println(isEven.accept(4)) // true println(isEvenLambda.accept(4)) // true}🔄 SAM Conversions
Phần tiêu đề “🔄 SAM Conversions”SAM Conversion cho phép Kotlin tự động convert lambda thành instance của functional interface:
fun interface Transformer<T, R> { fun transform(input: T): R}
fun main() { // Các cách tạo instance tương đương:
// 1. Anonymous object (verbose) val toUpperCase1 = object : Transformer<String, String> { override fun transform(input: String) = input.uppercase() }
// 2. SAM với explicit parameter val toUpperCase2 = Transformer<String, String> { input -> input.uppercase() }
// 3. SAM với it (cleanest) val toUpperCase3 = Transformer<String, String> { it.uppercase() }
println(toUpperCase1.transform("hello")) // HELLO println(toUpperCase2.transform("hello")) // HELLO println(toUpperCase3.transform("hello")) // HELLO}📝 Khai báo fun interface
Phần tiêu đề “📝 Khai báo fun interface”Cú pháp
Phần tiêu đề “Cú pháp”fun interface Runnable { fun run()}
fun interface Consumer<T> { fun consume(item: T)}
fun interface Mapper<T, R> { fun map(input: T): R}
fun interface BiFunction<T, U, R> { fun apply(first: T, second: U): R}Quy tắc
Phần tiêu đề “Quy tắc”🎯 Sử dụng với Functions
Phần tiêu đề “🎯 Sử dụng với Functions”fun interface IntOperation { fun execute(n: Int): Int}
fun processNumber(n: Int, operation: IntOperation): Int { return operation.execute(n)}
fun main() { // Truyền lambda trực tiếp nhờ SAM conversion val result1 = processNumber(5) { it * 2 } val result2 = processNumber(5) { it + 10 } val result3 = processNumber(5) { it * it }
println(result1) // 10 println(result2) // 15 println(result3) // 25}Callback Pattern
Phần tiêu đề “Callback Pattern”fun interface OnCompletionListener { fun onComplete(result: String)}
class DataLoader { fun loadData(callback: OnCompletionListener) { Thread.sleep(1000) // Simulate loading callback.onComplete("Data loaded successfully") }}
fun main() { val loader = DataLoader()
// SAM conversion - clean callback syntax loader.loadData { result -> println("Callback received: $result") }}☕ Java Interoperability
Phần tiêu đề “☕ Java Interoperability”Kotlin tự động áp dụng SAM conversion cho Java interfaces có một abstract method:
// Java interfacepublic interface OnClickListener { void onClick(View view);}
public class Button { public void setOnClickListener(OnClickListener listener) { // ... }}// Kotlin sử dụngval button = Button()
// Java style (verbose)button.setOnClickListener(object : OnClickListener { override fun onClick(view: View) { println("Clicked!") }})
// SAM conversion (concise)button.setOnClickListener { view -> println("Clicked!")}
// Trailing lambda syntaxbutton.setOnClickListener { println("Clicked!")}Common Java SAM Interfaces
Phần tiêu đề “Common Java SAM Interfaces”| Java Interface | Kotlin Lambda |
|---|---|
Runnable |
{ println("Running") } |
Callable<T> |
{ computeValue() } |
Comparator<T> |
{ a, b -> a - b } |
OnClickListener |
{ view -> handle(view) } |
Predicate<T> |
{ it > 0 } |
Consumer<T> |
{ println(it) } |
Function<T, R> |
{ it.toString() } |
🆚 fun interface vs typealias
Phần tiêu đề “🆚 fun interface vs typealias”Cả hai có thể đại diện cho function:
// Approach 1: Functional Interfacefun interface Validator { fun validate(input: String): Boolean}
// Approach 2: Type Aliastypealias ValidatorAlias = (String) -> BooleanSo sánh
Phần tiêu đề “So sánh”| Feature | fun interface | typealias |
|---|---|---|
| Tạo kiểu mới | ✅ Yes | ❌ No (chỉ alias) |
| Có thể extend | ✅ Yes | ❌ No |
| Default methods | ✅ Yes | ❌ No |
| Java interop | ✅ Great | ❌ Limited |
| Performance | Có thể tạo wrapper | Pure function |
| Type safety | Stronger | Weaker |
Khi nào dùng gì?
Phần tiêu đề “Khi nào dùng gì?”// ✅ Dùng fun interface khi:// - Cần tương thích với Java// - Muốn kiểu dữ liệu riêng biệt// - Cần default methods// - Xây dựng API library
fun interface NetworkCallback<T> { fun onResult(data: T) fun onError(error: Throwable) = println("Error: ${error.message}") // Default}
// ✅ Dùng typealias khi:// - Chỉ cần function đơn giản// - Internal code, không expose API// - Performance critical// - Kotlin-only codebase
typealias Predicate<T> = (T) -> Booleantypealias Mapper<T, R> = (T) -> Rtypealias Action = () -> Unit🛠️ Thực hành
Phần tiêu đề “🛠️ Thực hành”Bài tập: Tạo event system với SAM interfaces
// TODO: Tạo:// - fun interface EventHandler<T>// - class EventBus với register và emitLời giải:
fun interface EventHandler<T> { fun handle(event: T)}
class EventBus<T> { private val handlers = mutableListOf<EventHandler<T>>()
fun register(handler: EventHandler<T>) { handlers.add(handler) }
// SAM-friendly register with lambda fun onEvent(handler: (T) -> Unit) { handlers.add(EventHandler { handler(it) }) }
fun emit(event: T) { handlers.forEach { it.handle(event) } }
fun clear() = handlers.clear()}
// Usagedata class UserCreatedEvent(val userId: String, val userName: String)
fun main() { val userEvents = EventBus<UserCreatedEvent>()
// Register với SAM conversion userEvents.register { event -> println("Handler 1: User created - ${event.userName}") }
userEvents.onEvent { event -> println("Handler 2: Sending welcome email to ${event.userId}") }
// Emit event userEvents.emit(UserCreatedEvent("user-123", "Alice")) // Handler 1: User created - Alice // Handler 2: Sending welcome email to user-123}📱 Trong Android
Phần tiêu đề “📱 Trong Android”// Custom callback interfacefun interface OnItemSelectedListener<T> { fun onSelected(item: T, position: Int)}
class ItemAdapter<T>( private val items: List<T>, private val listener: OnItemSelectedListener<T>) : RecyclerView.Adapter<ItemViewHolder>() {
override fun onBindViewHolder(holder: ItemViewHolder, position: Int) { holder.itemView.setOnClickListener { listener.onSelected(items[position], position) } }}
// Usage với SAM conversionval adapter = ItemAdapter(users) { user, position -> println("Selected: ${user.name} at $position")}
// Compose-style callbacksfun interface OnClick { fun onClick()}
@Composablefun Button(text: String, onClick: OnClick) { // ...}
// SAM in ComposeButton("Click me") { println("Clicked!")}⚠️ Lưu ý quan trọng
Phần tiêu đề “⚠️ Lưu ý quan trọng”✅ Checklist - Tự kiểm tra
Phần tiêu đề “✅ Checklist - Tự kiểm tra”Sau bài học này, bạn có thể:
- Hiểu Functional Interface (SAM) là gì
- Khai báo
fun interfacetrong Kotlin - Sử dụng SAM conversion với lambda
- Áp dụng SAM cho Java interfaces
- Phân biệt
fun interfacevstypealias - Biết khi nào dùng functional interface
Tiếp theo: Higher-Order Functions