Type Aliases trong Kotlin
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
typealias UserId = Long
typealias Username = String
typealias 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"
val email: Email = "alice@example.com"
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
Rút gọn các function type phức tạp:
// Dài và khó đọc
fun process(callback: (String, Int, Boolean) -> List<String>) { }
// Với type alias - dễ đọc hơn
typealias StringProcessor = (String, Int, Boolean) -> List<String>
fun processWithAlias(callback: StringProcessor) { }
// Ví dụ thực tế
typealias ClickHandler = (view: View, x: Int, y: Int) -> Unit
typealias OnComplete<T> = (Result<T>) -> Unit
typealias Predicate<T> = (T) -> Boolean
typealias 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
Rút gọn generic types phức tạp:
// Map phức tạp
typealias UserMap = Map<String, List<User>>
typealias StringMap = Map<String, String>
typealias Callback<T> = (T) -> Unit
// Nested generics
typealias 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
class Outer {
inner class Inner
}
// Type alias cho inner class
typealias 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
// Common collection aliases
typealias StringList = List<String>
typealias IntSet = Set<Int>
typealias StringIntMap = Map<String, Int>
// Mutable versions
typealias MutableStringList = MutableList<String>
typealias MutableIntSet = MutableSet<Int>
// Domain-specific
typealias 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
typealias HtmlBuilder = StringBuilder.() -> Unit
typealias 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
import kotlinx.coroutines.*
typealias SuspendAction = suspend () -> Unit
typealias SuspendCallback<T> = suspend (T) -> Unit
typealias 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
// Cho Java interop
typealias 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ế
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>) -> Unit
typealias 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
typealias EventListener<T> = (T) -> Unit
typealias 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
typealias Query = String
typealias 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
// Type alias - KHÔNG type-safe
typealias UserId = Long
typealias ProductId = Long
fun processUser(id: UserId) {}
fun processProduct(id: ProductId) {}
// ❌ Không có lỗi compile - bug tiềm ẩn!
val userId: UserId = 1L
val productId: ProductId = 2L
processUser(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
| 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
Last updated on