Kotlin 2.3 cho Android: Name-Based Destructuring, KMP và Câu Hỏi Phỏng Vấn 2026
Câu hỏi phỏng vấn Kotlin 2.3 dành cho lập trình viên Android năm 2026. Name-based destructuring, KMP, context parameters, Flow và coroutines kèm ví dụ code.

Câu hỏi phỏng vấn Kotlin 2.3 hiện đang nằm trong nhóm chủ đề được tìm kiếm nhiều nhất cho vị trí lập trình viên Android trong năm 2026. Với sự xuất hiện của name-based destructuring declarations, việc ổn định hóa context parameters và hệ sinh thái Kotlin Multiplatform đã trưởng thành, nhà tuyển dụng mong đợi ứng viên thể hiện sự thành thạo với các tính năng mới này.
Kotlin 2.3.20, phát hành tháng 3 năm 2026, giới thiệu name-based destructuring declarations, thay đổi giải quyết overload cho context parameters và cải thiện khả năng tương tác KMP với C và TypeScript. Những tính năng này thường xuất hiện trong các buổi phỏng vấn Android cấp cao.
Name-Based Destructuring: Câu Hỏi Phỏng Vấn Kotlin 2.3 Phổ Biến Nhất
Trước Kotlin 2.3, destructuring declarations dựa vào các hàm componentN() theo vị trí. Điều này tạo ra một cạm bẫy nổi tiếng: việc hoán đổi thứ tự thuộc tính trong data class sẽ âm thầm làm hỏng các destructuring hiện có.
Câu hỏi phỏng vấn kinh điển trình bày tình huống sau:
data class User(val username: String, val email: String)
fun main() {
val user = User("alice", "alice@example.com")
// Position-based: thứ tự quan trọng, không phải tên
val (email, username) = user
println(email) // In ra "alice" -- sai!
println(username) // In ra "alice@example.com" -- sai!
}Kotlin 2.3.20 giải quyết vấn đề này với name-based destructuring. Ba chế độ compiler điều khiển hành vi:
kotlin {
compilerOptions {
// Chế độ 1: Chỉ cú pháp tường minh
freeCompilerArgs.add("-Xname-based-destructuring=only-syntax")
// Chế độ 2: Cảnh báo khi tên không khớp
// freeCompilerArgs.add("-Xname-based-destructuring=name-mismatch")
// Chế độ 3: Name-based đầy đủ làm mặc định
// freeCompilerArgs.add("-Xname-based-destructuring=complete")
}
}Với chế độ complete, dấu ngoặc đơn sử dụng khớp theo tên, và dấu ngoặc vuông giữ lại hành vi theo vị trí:
data class User(val username: String, val email: String)
fun main() {
val user = User("alice", "alice@example.com")
// Name-based: khớp theo tên thuộc tính
val (email, username) = user
println(email) // "alice@example.com" -- đúng
println(username) // "alice" -- đúng
// Position-based: ngoặc vuông giữ hành vi cũ
val [first, second] = user
println(first) // "alice" (component1)
println(second) // "alice@example.com" (component2)
}Một câu trả lời tốt trong phỏng vấn giải thích cả ba chế độ và xác định khi nào mỗi chế độ được áp dụng: only-syntax cho việc chuyển đổi dần, name-mismatch để phát hiện lỗi trong code hiện có, và complete cho dự án mới.
Câu Hỏi Phỏng Vấn Kotlin Multiplatform cho Lập Trình Viên Android
KMP đã vượt qua giai đoạn thử nghiệm. Google chính thức hỗ trợ KMP để chia sẻ logic nghiệp vụ giữa Android và iOS, và hầu hết các dự án multi-platform mới đều áp dụng kiến trúc shared-core làm mặc định.
Câu hỏi phỏng vấn điển hình hỏi: "Làm thế nào để cấu trúc một module KMP cho shared networking?"
import io.ktor.client.*
import io.ktor.client.request.*
import io.ktor.client.statement.*
import kotlinx.serialization.Serializable
import kotlinx.serialization.json.Json
@Serializable
data class UserProfile(
val id: String,
val displayName: String,
val avatarUrl: String?
)
// Pattern expect/actual cho HTTP engine theo nền tảng
expect fun createHttpClient(): HttpClient
class ApiClient {
private val client = createHttpClient()
private val json = Json { ignoreUnknownKeys = true }
// Hàm suspend chia sẻ giữa Android và iOS
suspend fun fetchProfile(userId: String): UserProfile {
val response = client.get("https://api.example.com/users/$userId")
return json.decodeFromString(response.bodyAsText())
}
}import io.ktor.client.*
import io.ktor.client.engine.okhttp.*
// Android sử dụng engine OkHttp
actual fun createHttpClient(): HttpClient = HttpClient(OkHttp) {
engine {
config {
retryOnConnectionFailure(true)
}
}
}Nhà tuyển dụng đánh giá liệu ứng viên có hiểu cơ chế expect/actual, các dependency theo nền tảng, và ranh giới giữa code chia sẻ và code nền tảng hay không.
Một câu hỏi phổ biến khác liên quan đến Swift Export, vốn đã phát triển đáng kể trong năm 2026. Các hàm suspend của Kotlin giờ đây được ánh xạ trực tiếp sang async/await của Swift, loại bỏ nhu cầu về wrapper completion handler.
Context Parameters: Thay Thế Context Receivers
Context parameters thay thế tính năng context receivers đã bị đánh dấu lỗi thời. Thay đổi này thường xuất hiện dưới dạng câu hỏi mẹo trong phỏng vấn vì nhiều lập trình viên vẫn sử dụng cú pháp cũ.
class Logger {
fun log(message: String) = println("[LOG] $message")
}
class Transaction {
fun execute(query: String) = println("Executing: $query")
}
// Kotlin 2.3: context parameters (ổn định)
context(logger: Logger, tx: Transaction)
fun saveUser(id: Int) {
logger.log("Saving user $id")
tx.execute("INSERT INTO users (id) VALUES ($id)")
}
fun main() {
val logger = Logger()
val tx = Transaction()
context(logger, tx) {
saveUser(42)
}
}Thay đổi quan trọng trong Kotlin 2.3.20: giải quyết overload cho context parameters giờ đây tuân theo các quy tắc khác. Hàm có context parameters không còn tự động được ưu tiên hơn hàm không có context:
class Logger {
fun log(message: String) = println("[LOG] $message")
}
// Không có context parameter
fun saveUser(id: Int) {
println("Saving user $id (no logger)")
}
// Có context parameter
context(logger: Logger)
fun saveUser(id: Int) {
logger.log("Saving user $id")
println("Saving user $id (no logger)")
}
fun main() {
val logger = Logger()
// Điều này giờ tạo ra lỗi nhập nhằng trong Kotlin 2.3.20
// context(logger) { saveUser(1) }
// Sửa: đổi tên hoặc chỉ định rõ biến thể context-aware
}Nhà tuyển dụng sử dụng chủ đề này để đánh giá nhận thức về breaking changes và chiến lược di cư. Điểm mấu chốt: việc dựa vào giải quyết implicit context parameter để shadow các overload không có context không còn hoạt động.
Coroutines và Flow: Vẫn Là Chủ Đề Phỏng Vấn Android Hàng Đầu
Mặc dù Kotlin 2.3 giới thiệu các tính năng mới, coroutines và Flow vẫn là lĩnh vực được kiểm tra nhiều nhất. Câu hỏi phỏng vấn thường thăm dò sự khác biệt giữa cold và hot flow, structured concurrency và cancellation.
import kotlinx.coroutines.*
import kotlinx.coroutines.flow.*
// Cold Flow: chỉ emit khi được collect
fun userUpdates(): Flow<String> = flow {
println("Flow started") // Chạy cho mỗi collector
emit("User logged in")
delay(1000)
emit("User updated profile")
}
// Hot Flow: emit độc lập với collector
class UserRepository {
// StateFlow giữ giá trị mới nhất
private val _state = MutableStateFlow("idle")
val state: StateFlow<String> = _state.asStateFlow()
// SharedFlow phát đến nhiều collector
private val _events = MutableSharedFlow<String>()
val events: SharedFlow<String> = _events.asSharedFlow()
suspend fun login() {
_state.value = "loading"
delay(500)
_state.value = "authenticated"
_events.emit("login_success")
}
}Câu hỏi tiếp theo thường hỏi về việc collect flow an toàn trong Android:
import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope
import kotlinx.coroutines.flow.*
import kotlinx.coroutines.launch
class UserViewModel(private val repo: UserRepository) : ViewModel() {
// Cung cấp state cho Compose UI
val uiState: StateFlow<String> = repo.state
.stateIn(
scope = viewModelScope,
started = SharingStarted.WhileSubscribed(5000),
initialValue = "idle"
)
init {
// Collect events an toàn trong phạm vi ViewModel
viewModelScope.launch {
repo.events.collect { event ->
// Xử lý sự kiện một lần
println("Event received: $event")
}
}
}
}Chiến lược WhileSubscribed(5000) giữ upstream hoạt động trong 5 giây sau khi subscriber cuối cùng biến mất, ngăn chặn việc khởi động lại không cần thiết trong quá trình thay đổi cấu hình như xoay màn hình.
Sẵn sàng chinh phục phỏng vấn Android?
Luyện tập với mô phỏng tương tác, flashcards và bài kiểm tra kỹ thuật.
Sealed Classes và Exhaustive When: Pattern Matching trong Phỏng Vấn
Sealed classes xuất hiện trong hầu hết mọi buổi phỏng vấn Android. Câu hỏi thường yêu cầu ứng viên mô hình hóa UI state:
sealed interface UiState<out T> {
data object Loading : UiState<Nothing>
data class Success<T>(val data: T) : UiState<T>
data class Error(val message: String, val retry: (() -> Unit)? = null) : UiState<Nothing>
}
// Sử dụng trong ViewModel
fun loadUsers(): Flow<UiState<List<UserProfile>>> = flow {
emit(UiState.Loading)
try {
val users = apiClient.fetchUsers()
emit(UiState.Success(users))
} catch (e: Exception) {
emit(UiState.Error(
message = e.localizedMessage ?: "Unknown error",
retry = { /* trigger reload */ }
))
}
}Trong Kotlin, biểu thức when trên sealed types là exhaustive tại thời điểm biên dịch. Việc thêm subtype mới buộc phải xử lý nó ở mọi nơi, loại bỏ toàn bộ một danh mục lỗi runtime. Nhà tuyển dụng đặc biệt kiểm tra xem ứng viên có sử dụng sealed interface (không phải sealed class) cho pattern này không, vì interface cho phép cấu trúc phân cấp linh hoạt hơn.
Kotlin/Wasm và Các Target Biên Dịch Cross-Platform
Kotlin/Wasm đã đạt trạng thái Beta và mang lại hiệu suất gần native trên trình duyệt. Mặc dù chưa phải là mối quan tâm hàng ngày của lập trình viên Android, nhà tuyển dụng tại các công ty có sản phẩm web+mobile thường hỏi về nó.
Thông tin quan trọng cho phỏng vấn:
- Kotlin 2.3.20 mang lại nội suy chuỗi nhanh hơn 4,6 lần trong Wasm thông qua JS String builtins
- Kích thước binary giảm khoảng 5%
- Clean build chạy nhanh hơn 65%, incremental build nhanh hơn 21%
- Annotation
@nativeInvokecho phép đối tượng Kotlin hoạt động như hàm JavaScript
Kotlin/JS cũng đã có khả năng implement Kotlin interface từ TypeScript, loại bỏ hạn chế trước đó. Kết hợp với hỗ trợ SWC cho transpilation, Kotlin giờ đây bao phủ Android, iOS (qua KMP), Web (qua Wasm/JS) và server-side từ một codebase duy nhất.
Scope Functions: Bộ Lọc Phỏng Vấn Tưởng Đơn Giản Nhưng Lại Khó
Scope functions (let, run, with, apply, also) trông đơn giản nhưng là yếu tố phân biệt lập trình viên Kotlin có kinh nghiệm. Câu hỏi phỏng vấn thường hỏi: "Khi nào sử dụng let và khi nào sử dụng apply?"
data class Request(
var url: String = "",
var method: String = "GET",
var headers: MutableMap<String, String> = mutableMapOf(),
var body: String? = null
)
fun buildRequest(): Request {
// apply: cấu hình đối tượng, trả về đối tượng
return Request().apply {
url = "https://api.example.com/users"
method = "POST"
headers["Content-Type"] = "application/json"
body = "{\"name\": \"Alice\"}"
}
}
fun processNullable(input: String?) {
// let: biến đổi nullable, trả về kết quả lambda
val length = input?.let { value ->
println("Processing: $value")
value.trim().length
} ?: 0
// also: hiệu ứng phụ, trả về đối tượng gốc
input?.also { println("Logging input: $it") }
// run: thực thi khối với receiver, trả về kết quả lambda
val result = input?.run {
// 'this' là string
uppercase().take(10)
}
}Khung quyết định: apply cho cấu hình đối tượng (trả về receiver), let cho biến đổi null-safe (trả về kết quả lambda), also cho hiệu ứng phụ (trả về receiver), run cho tính toán kết quả với ngữ cảnh receiver.
Null Safety và Nguy Hiểm của Double-Bang
Null safety vẫn là chủ đề phỏng vấn cơ bản. Câu hỏi "Tại sao !! nguy hiểm?" kiểm tra xem ứng viên có thực sự hiểu hệ thống kiểu của Kotlin hay không.
fun findUser(id: String): User? {
// Trả về null nếu không tìm thấy user
return database.queryUser(id)
}
// Xấu: !! đánh bại mục đích của null safety
fun riskyApproach(id: String) {
val user = findUser(id)!! // NullPointerException nếu null
println(user.username)
}
// Tốt: xử lý null rõ ràng
fun safeApproach(id: String) {
val user = findUser(id) ?: run {
println("User not found")
return
}
println(user.username)
}
// Tốt: cung cấp giá trị mặc định với Elvis operator
fun displayName(id: String): String {
return findUser(id)?.username ?: "Anonymous"
}Câu trả lời đầy đủ cần đề cập rằng !! chỉ nên xuất hiện trong code kiểm thử hoặc tại ranh giới interop nền tảng nơi mà null thực sự không thể xảy ra.
Bắt đầu luyện tập!
Kiểm tra kiến thức với mô phỏng phỏng vấn và bài kiểm tra kỹ thuật.
Kết Luận
- Name-based destructuring trong Kotlin 2.3.20 loại bỏ lỗi phụ thuộc vị trí. Cần nắm vững cả ba chế độ compiler:
only-syntax,name-mismatch,complete - KMP đã ổn định trong môi trường production năm 2026. Chuẩn bị câu hỏi về expect/actual, kiến trúc shared module và tương tác Swift Export
- Context parameters thay thế context receivers. Thay đổi giải quyết overload trong 2.3.20 là câu hỏi mẹo phổ biến
- Coroutines và Flow thống trị phỏng vấn Android. Thành thạo
StateFlowvsSharedFlow,WhileSubscribedvà structured concurrency - Sealed interfaces mô hình hóa UI state với tính exhaustive tại thời điểm biên dịch. Ưu tiên
sealed interfacehơnsealed class - Scope functions đòi hỏi khung quyết định rõ ràng: receiver vs argument, giá trị trả về vs receiver
- Null safety là không thể thương lượng. Trình bày rõ tại sao
!!làm suy yếu hệ thống kiểu và thể hiện các phương án thay thế
Bắt đầu luyện tập!
Kiểm tra kiến thức với mô phỏng phỏng vấn và bài kiểm tra kỹ thuật.
Thẻ
Chia sẻ
Bài viết liên quan

20 Câu Hỏi Phỏng Vấn Jetpack Compose Hàng Đầu Năm 2026
20 câu hỏi phỏng vấn Jetpack Compose thường gặp nhất: recomposition, quản lý state, navigation, hiệu năng và các pattern kiến trúc với ví dụ code chi tiết.

Kotlin Coroutines cho Android: Huong dan day du 2026
Huong dan toan dien ve Kotlin coroutines trong phat trien Android: suspend functions, scopes, dispatchers va cac pattern nang cao.

Jetpack Compose: Hoạt ảnh nâng cao từng bước
Hướng dẫn đầy đủ về hoạt ảnh nâng cao trong Compose: chuyển tiếp, AnimatedVisibility, Animatable, cử chỉ và hiệu năng cho giao diện Android mượt mà.