Skip to Content

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 CaseExample
Function typestypealias Handler = (Int) -> Unit
Generic typestypealias StringMap = Map<String, String>
Collectionstypealias Tags = Set<String>
Lambdas with receivertypealias Builder = T.() -> Unit
Suspend functionstypealias 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