Functional (SAM) Interfaces trong Kotlin
🎯 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ì?
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”?
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 - verbose
val 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
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
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
fun interface chỉ có 1 abstract method:
// ✅ OK
fun interface Valid {
fun doSomething()
}
// ❌ Lỗi compile - nhiều hơn 1 abstract method
fun interface Invalid {
fun method1()
fun method2()
}
// ✅ OK - có thể có default methods
fun interface AlsoValid {
fun doSomething()
fun defaultMethod() = println("Default") // Không tính là abstract
}🎯 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
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
Kotlin tự động áp dụng SAM conversion cho Java interfaces có một abstract method:
// Java interface
public interface OnClickListener {
void onClick(View view);
}
public class Button {
public void setOnClickListener(OnClickListener listener) {
// ...
}
}// Kotlin sử dụng
val 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 syntax
button.setOnClickListener {
println("Clicked!")
}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
Cả hai có thể đại diện cho function:
// Approach 1: Functional Interface
fun interface Validator {
fun validate(input: String): Boolean
}
// Approach 2: Type Alias
typealias ValidatorAlias = (String) -> BooleanSo 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ì?
// ✅ 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) -> Boolean
typealias Mapper<T, R> = (T) -> R
typealias Action = () -> Unit🛠️ 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()
}
// Usage
data 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
// Custom callback interface
fun 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 conversion
val adapter = ItemAdapter(users) { user, position ->
println("Selected: ${user.name} at $position")
}
// Compose-style callbacks
fun interface OnClick {
fun onClick()
}
@Composable
fun Button(text: String, onClick: OnClick) {
// ...
}
// SAM in Compose
Button("Click me") {
println("Clicked!")
}⚠️ Lưu ý quan trọng
SAM conversion chỉ hoạt động khi:
- Interface là
fun interface(Kotlin) hoặc SAM interface (Java) - Có đúng 1 abstract method
- Lambda được truyền trực tiếp nơi interface được expect
Không dùng cho Kotlin interfaces thường:
// ❌ Không phải fun interface - SAM không hoạt động
interface RegularInterface {
fun method()
}
// Phải dùng object expression
val impl = object : RegularInterface {
override fun method() = println("Hello")
}
// ✅ Dùng fun interface
fun interface FunctionalInterface {
fun method()
}
val impl2 = FunctionalInterface { println("Hello") } // ✅ SAM works✅ 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