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

λ 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 (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
}

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

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
}

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

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

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) -> Boolean
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
// ✅ 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

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à emit

Lờ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
}

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


Sau bài học này, bạn có thể:

  • Hiểu Functional Interface (SAM) là gì
  • Khai báo fun interface trong Kotlin
  • Sử dụng SAM conversion với lambda
  • Áp dụng SAM cho Java interfaces
  • Phân biệt fun interface vs typealias
  • Biết khi nào dùng functional interface

Tiếp theo: Higher-Order Functions