🏷️ Type Aliases
1. Giới thiệu
Phần tiêu đề “1. Giới thiệu”Type aliases cho phép đặt tên thay thế cho các kiểu dữ liệu có sẵn. Chúng hữu ích để rút gọn các kiểu phức tạp và làm code dễ đọc hơn.
2. Cú pháp cơ bản
Phần tiêu đề “2. Cú pháp cơ bản”typealias UserId = Longtypealias Username = Stringtypealias Email = String
fun createUser(id: UserId, name: Username, email: Email) { println("Creating user $id: $name ($email)")}
fun main() { val userId: UserId = 123L val username: Username = "alice"
createUser(userId, username, email)}Lưu ý: Type alias KHÔNG tạo kiểu mới - chỉ là tên khác cho kiểu có sẵn.
3. Function Types
Phần tiêu đề “3. Function Types”Rút gọn các function type phức tạp:
// Dài và khó đọcfun process(callback: (String, Int, Boolean) -> List<String>) { }
// Với type alias - dễ đọc hơntypealias StringProcessor = (String, Int, Boolean) -> List<String>fun processWithAlias(callback: StringProcessor) { }
// Ví dụ thực tếtypealias ClickHandler = (view: View, x: Int, y: Int) -> Unittypealias OnComplete<T> = (Result<T>) -> Unittypealias Predicate<T> = (T) -> Booleantypealias Mapper<T, R> = (T) -> R
fun <T> filter(items: List<T>, predicate: Predicate<T>): List<T> { return items.filter(predicate)}
fun main() { val numbers = listOf(1, 2, 3, 4, 5) val isEven: Predicate<Int> = { it % 2 == 0 }
println(filter(numbers, isEven)) // [2, 4]}4. Generic Types
Phần tiêu đề “4. Generic Types”Rút gọn generic types phức tạp:
// Map phức tạptypealias UserMap = Map<String, List<User>>typealias StringMap = Map<String, String>typealias Callback<T> = (T) -> Unit
// Nested genericstypealias Table<K, V> = MutableMap<K, MutableList<V>>
data class User(val id: Int, val name: String)
fun loadUsers(): UserMap { return mapOf( "admins" to listOf(User(1, "Alice")), "users" to listOf(User(2, "Bob"), User(3, "Charlie")) )}
fun main() { val users: UserMap = loadUsers() println(users["admins"]) // [User(id=1, name=Alice)]
val table: Table<String, Int> = mutableMapOf() table["numbers"] = mutableListOf(1, 2, 3) println(table) // {numbers=[1, 2, 3]}}5. Inner và Nested Classes
Phần tiêu đề “5. Inner và Nested Classes”class Outer { inner class Inner}
// Type alias cho inner classtypealias OuterInner = Outer.Inner
class Container { class Item(val value: String)}
typealias ContainerItem = Container.Item
fun main() { val item: ContainerItem = Container.Item("Hello") println(item.value)}6. Collection Types
Phần tiêu đề “6. Collection Types”// Common collection aliasestypealias StringList = List<String>typealias IntSet = Set<Int>typealias StringIntMap = Map<String, Int>
// Mutable versionstypealias MutableStringList = MutableList<String>typealias MutableIntSet = MutableSet<Int>
// Domain-specifictypealias Tags = Set<String>typealias Headers = Map<String, String>typealias Matrix = Array<IntArray>
fun processHeaders(headers: Headers) { headers.forEach { (key, value) -> println("$key: $value") }}
fun main() { val tags: Tags = setOf("kotlin", "android", "programming") val headers: Headers = mapOf( "Content-Type" to "application/json", "Authorization" to "Bearer token123" )
processHeaders(headers)}7. Lambda with Receiver
Phần tiêu đề “7. Lambda with Receiver”typealias HtmlBuilder = StringBuilder.() -> Unittypealias Configure<T> = T.() -> Unit
fun html(builder: HtmlBuilder): String { return StringBuilder().apply(builder).toString()}
fun <T> configure(target: T, block: Configure<T>): T { target.block() return target}
fun main() { val result = html { append("<html>") append("<body>Hello</body>") append("</html>") } println(result)
val list = configure(mutableListOf<Int>()) { add(1) add(2) add(3) } println(list) // [1, 2, 3]}8. Suspending Functions
Phần tiêu đề “8. Suspending Functions”import kotlinx.coroutines.*
typealias SuspendAction = suspend () -> Unittypealias SuspendCallback<T> = suspend (T) -> Unittypealias AsyncOperation<T> = suspend () -> T
suspend fun runWithRetry( times: Int, action: SuspendAction) { repeat(times) { try { action() return } catch (e: Exception) { delay(1000) } }}
fun main() = runBlocking { val fetchData: AsyncOperation<String> = { delay(100) "Data loaded" }
println(fetchData())}9. Platform Types và Interop
Phần tiêu đề “9. Platform Types và Interop”// Cho Java interoptypealias JavaList<T> = java.util.ArrayList<T>typealias JavaMap<K, V> = java.util.HashMap<K, V>
// Android-specific// typealias ColorInt = Int// typealias LayoutRes = Int// typealias StringRes = Int
fun main() { val javaList: JavaList<String> = JavaList() javaList.add("Hello") javaList.add("World")
println(javaList)}10. Use Cases thực tế
Phần tiêu đề “10. Use Cases thực tế”API Response handlers
Phần tiêu đề “API Response handlers”sealed class Result<out T> { data class Success<T>(val data: T) : Result<T>() data class Error(val message: String) : Result<Nothing>()}
typealias ApiResponse<T> = Result<T>typealias ApiHandler<T> = (ApiResponse<T>) -> Unittypealias NetworkCall<T> = suspend () -> ApiResponse<T>
fun <T> handleApiCall( response: ApiResponse<T>, onSuccess: (T) -> Unit, onError: (String) -> Unit) { when (response) { is Result.Success -> onSuccess(response.data) is Result.Error -> onError(response.message) }}Event handlers
Phần tiêu đề “Event handlers”typealias EventListener<T> = (T) -> Unittypealias ClickListener = EventListener<View>typealias TextChangeListener = EventListener<String>typealias ItemClickListener<T> = (position: Int, item: T) -> Unit
class View
class EventBus<T> { private val listeners = mutableListOf<EventListener<T>>()
fun subscribe(listener: EventListener<T>) { listeners.add(listener) }
fun emit(event: T) { listeners.forEach { it(event) } }}Database queries
Phần tiêu đề “Database queries”typealias Query = Stringtypealias Parameters = Map<String, Any?>typealias RowMapper<T> = (ResultRow) -> T
class ResultRow(private val data: Map<String, Any?>) { operator fun get(column: String): Any? = data[column]}
fun <T> executeQuery( query: Query, params: Parameters, mapper: RowMapper<T>): List<T> { // Execute and map results return emptyList()}
data class User(val id: Int, val name: String)
fun main() { val userMapper: RowMapper<User> = { row -> User( id = row["id"] as Int, name = row["name"] as String ) }
val users = executeQuery( "SELECT * FROM users WHERE active = :active", mapOf("active" to true), userMapper )}11. Type Alias vs Value Class
Phần tiêu đề “11. Type Alias vs Value Class”// Type alias - KHÔNG type-safetypealias UserId = Longtypealias ProductId = Long
fun processUser(id: UserId) {}fun processProduct(id: ProductId) {}
// ❌ Không có lỗi compile - bug tiềm ẩn!val userId: UserId = 1Lval productId: ProductId = 2LprocessUser(productId) // Compiles but wrong!
// Value class - type-safe@JvmInline value class SafeUserId(val value: Long)@JvmInline value class SafeProductId(val value: Long)
fun processSafeUser(id: SafeUserId) {}
// ✅ Compile error prevents bug// processSafeUser(SafeProductId(2L)) // Error!Khi nào dùng type alias:
- Rút gọn types phức tạp
- Làm code dễ đọc
- KHÔNG cần type safety
Khi nào dùng value class:
- Cần type safety
- Prevent mixing up similar types
- Domain primitives
📝 Tóm tắt
Phần tiêu đề “📝 Tóm tắt”| Use Case | Example |
|---|---|
| Function types | typealias Handler = (Int) -> Unit |
| Generic types | typealias StringMap = Map<String, String> |
| Collections | typealias Tags = Set<String> |
| Lambdas with receiver | typealias Builder = T.() -> Unit |
| Suspend functions | typealias AsyncOp = suspend () -> T |
Best practices:
- Dùng type alias cho readability, không phải type-safety
- Đặt tên có ý nghĩa trong domain
- Đừng lạm dụng - chỉ dùng khi thực sự làm code dễ đọc hơn
- Consider value classes khi cần type safety