Kotlin 2.3 Android面接対策:名前ベースの分割代入、KMP、頻出質問を徹底解説【2026年版】

2026年のAndroid開発者面接で問われるKotlin 2.3の新機能を網羅的に解説。名前ベースの分割代入、Kotlin Multiplatform、コンテキストパラメータ、Flowとコルーチンのコード例付き。

Kotlin 2.3 Android面接対策 2026

2026年現在、Kotlin 2.3の面接対策はAndroid開発者にとって最重要テーマの一つとなっている。名前ベースの分割代入宣言、安定化されたコンテキストパラメータ、そして成熟したKotlin Multiplatformエコシステムなど、面接官はこれらの新機能に対する深い理解を求めている。

Kotlin 2.3の主な変更点

2026年3月にリリースされたKotlin 2.3.20では、名前ベースの分割代入宣言、コンテキストパラメータのオーバーロード解決の変更、C・TypeScriptとのKMP相互運用性の向上が導入された。これらの機能はシニアAndroid面接で頻繁に出題される。

名前ベースの分割代入:Kotlin 2.3で最も出題される面接問題

Kotlin 2.3以前、分割代入宣言は位置ベースのcomponentN()関数に依存していた。これにより、データクラスのプロパティ順序を変更すると、既存の分割代入箇所が暗黙的に破壊されるという有名な落とし穴が存在していた。

典型的な面接問題は、次のシナリオを提示する:

User.ktkotlin
data class User(val username: String, val email: String)

fun main() {
    val user = User("alice", "alice@example.com")
    // Position-based: order matters, not names
    val (email, username) = user
    println(email)    // Prints "alice" -- wrong!
    println(username) // Prints "alice@example.com" -- wrong!
}

Kotlin 2.3.20では、名前ベースの分割代入によってこの問題が解決された。コンパイラには3つのモードが用意されている:

build.gradle.ktskotlin
kotlin {
    compilerOptions {
        // Mode 1: Explicit syntax only
        freeCompilerArgs.add("-Xname-based-destructuring=only-syntax")
        // Mode 2: Warns on name mismatch
        // freeCompilerArgs.add("-Xname-based-destructuring=name-mismatch")
        // Mode 3: Full name-based by default
        // freeCompilerArgs.add("-Xname-based-destructuring=complete")
    }
}

completeモードでは、丸括弧は名前ベースのマッチングを使用し、角括弧は位置ベースの動作を維持する:

NameBasedDestructuring.ktkotlin
data class User(val username: String, val email: String)

fun main() {
    val user = User("alice", "alice@example.com")

    // Name-based: matches by property name
    val (email, username) = user
    println(email)    // "alice@example.com" -- correct
    println(username) // "alice" -- correct

    // Position-based: square brackets preserve old behavior
    val [first, second] = user
    println(first)  // "alice" (component1)
    println(second) // "alice@example.com" (component2)
}

面接で高い評価を得る回答は、3つのモード全てを説明し、それぞれの適用場面を明確にする。only-syntaxは段階的な移行に、name-mismatchは既存コードのバグ検出に、completeは新規プロジェクトに適している。

Android開発者向けKotlin Multiplatform面接問題

KMPは実験段階を脱し、本格的な運用フェーズに入っている。GoogleはAndroidとiOS間のビジネスロジック共有を公式にサポートしており、新規のマルチプラットフォームプロジェクトのほとんどが共有コアアーキテクチャを採用している。

典型的な面接問題は「共有ネットワークモジュールをKMPでどのように設計するか」という質問である:

shared/src/commonMain/kotlin/com/app/network/ApiClient.ktkotlin
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?
)

// Expect/actual pattern for platform-specific HTTP engine
expect fun createHttpClient(): HttpClient

class ApiClient {
    private val client = createHttpClient()
    private val json = Json { ignoreUnknownKeys = true }

    // Suspend function shared across Android and iOS
    suspend fun fetchProfile(userId: String): UserProfile {
        val response = client.get("https://api.example.com/users/$userId")
        return json.decodeFromString(response.bodyAsText())
    }
}
shared/src/androidMain/kotlin/com/app/network/HttpClient.android.ktkotlin
import io.ktor.client.*
import io.ktor.client.engine.okhttp.*

// Android uses OkHttp engine
actual fun createHttpClient(): HttpClient = HttpClient(OkHttp) {
    engine {
        config {
            retryOnConnectionFailure(true)
        }
    }
}

面接官は、候補者がexpect/actualメカニズム、プラットフォーム固有の依存関係、共有コードとプラットフォームコードの境界を理解しているかを評価する。

もう一つの頻出問題はSwift Exportに関するものである。2026年にはSwift Exportが大幅に進化し、Kotlinのsuspend関数がSwiftのasync/awaitに直接マッピングされ、sealed classがSwiftの関連値付きenumにマッピングされるようになった。

コンテキストパラメータとオーバーロード解決の変更

コンテキストパラメータは、非推奨となったコンテキストレシーバーAPIを置き換えるものである。Kotlin 2.3.20では、オーバーロード解決に破壊的変更が導入された。コンテキストパラメータを持つ宣言は、それを持たない宣言よりも特定的とは見なされなくなった。

ContextParameters.ktkotlin
class Logger {
    fun info(msg: String) = println("INFO: $msg")
}

class TransactionScope {
    fun execute(block: () -> Unit) = block()
}

// Function with context parameter
context(logger: Logger)
fun saveUser(id: Int) {
    logger.info("Saving user $id")
}

// Overload without context -- now ambiguous in 2.3.20
fun saveUser(id: Int) {
    println("Saving user $id (no logger)")
}

fun main() {
    val logger = Logger()
    // This now produces an ambiguity error in Kotlin 2.3.20
    // context(logger) { saveUser(1) }

    // Fix: rename or qualify the context-aware variant
}

面接官は、この問題を通じて候補者の破壊的変更に対する認識と移行戦略を評価する。重要なポイントは、暗黙的なコンテキストパラメータ解決による非コンテキストオーバーロードのシャドウイングが機能しなくなったことである。

コルーチンとFlow:Android面接の最重要トピック

Kotlin 2.3で新機能が追加されたにもかかわらず、コルーチンとFlowは依然として最も出題頻度の高い分野である。面接では、コールドフローとホットフローの違い、構造化された並行性、キャンセレーションについて詳しく問われる。

FlowComparison.ktkotlin
import kotlinx.coroutines.*
import kotlinx.coroutines.flow.*

// Cold Flow: emits only when collected
fun userUpdates(): Flow<String> = flow {
    println("Flow started") // Runs per collector
    emit("User logged in")
    delay(1000)
    emit("User updated profile")
}

// Hot Flow: emits independently of collectors
class UserRepository {
    // StateFlow holds the latest value
    private val _state = MutableStateFlow("idle")
    val state: StateFlow<String> = _state.asStateFlow()

    // SharedFlow broadcasts to multiple collectors
    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")
    }
}

続く質問では、AndroidでFlowを安全に収集する方法が問われることが多い:

UserViewModel.ktkotlin
import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope
import kotlinx.coroutines.flow.*
import kotlinx.coroutines.launch

class UserViewModel(private val repo: UserRepository) : ViewModel() {

    // Expose state to Compose UI
    val uiState: StateFlow<String> = repo.state
        .stateIn(
            scope = viewModelScope,
            started = SharingStarted.WhileSubscribed(5000),
            initialValue = "idle"
        )

    init {
        // Collect events safely within ViewModel scope
        viewModelScope.launch {
            repo.events.collect { event ->
                // Handle one-time events
                println("Event received: $event")
            }
        }
    }
}

WhileSubscribed(5000)戦略は、最後のサブスクライバーが消えた後も5秒間上流をアクティブに保つ。これにより、画面回転などの構成変更時に不要な再起動を防ぐことができる。

Androidの面接対策はできていますか?

インタラクティブなシミュレーター、flashcards、技術テストで練習しましょう。

Sealedクラスと網羅的when式:面接でのパターンマッチング

Sealedクラスはほぼ全てのAndroid面接で出題される。典型的な質問では、UI状態のモデリングが求められる:

UiState.ktkotlin
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>
}

// Usage in 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 */ }
        ))
    }
}

Kotlinでは、sealed型に対するwhen式はコンパイル時に網羅性が検証される。新しいサブタイプを追加すると、全てのwhen式で処理が強制されるため、ランタイムバグの一カテゴリ全体が排除される。面接官は特に、このパターンでsealed classではなくsealed interfaceを使用するかどうかを確認する。インターフェースの方がより柔軟な階層構造を実現できるためである。

Kotlin/Wasmとクロスプラットフォームコンパイルターゲット

Kotlin/WasmはBetaステータスに到達し、ブラウザでネイティブに近いパフォーマンスを実現している。Android開発者の日常業務では直接的に関わる場面は少ないが、Web+モバイル製品を扱う企業の面接では問われることがある。

面接で押さえるべきポイント:

  • Kotlin 2.3.20ではJS String builtinsを通じてWasmでの文字列補間が4.6倍高速化
  • バイナリサイズが約5%削減
  • クリーンビルドが65%高速化、インクリメンタルビルドが21%高速化
  • @nativeInvokeアノテーションによりKotlinオブジェクトをJavaScript関数として動作させることが可能

Kotlin/JSでも、TypeScriptからKotlinインターフェースを実装する機能が追加され、以前の制限が解消された。SWCによるトランスパイルサポートと合わせて、Kotlinは単一コードベースからAndroid、iOS(KMP経由)、Web(Wasm/JS経由)、サーバーサイドの全ターゲットをカバーできるようになっている。

スコープ関数:見た目以上に差がつく面接のフィルター問題

スコープ関数(letrunwithapplyalso)は一見シンプルだが、Kotlin経験者とそうでない開発者を見分ける効果的な指標となる。典型的な面接質問は「letapplyをどのように使い分けるか」というものである。

ScopeFunctions.ktkotlin
data class Request(
    var url: String = "",
    var method: String = "GET",
    var headers: MutableMap<String, String> = mutableMapOf(),
    var body: String? = null
)

fun buildRequest(): Request {
    // apply: configure an object, returns the object
    return Request().apply {
        url = "https://api.example.com/users"
        method = "POST"
        headers["Content-Type"] = "application/json"
        body = "{\"name\": \"Alice\"}"
    }
}

fun processNullable(input: String?) {
    // let: transform nullable, returns lambda result
    val length = input?.let { value ->
        println("Processing: $value")
        value.trim().length
    } ?: 0

    // also: side effects, returns the original object
    input?.also { println("Logging input: $it") }

    // run: execute a block with receiver, returns lambda result
    val result = input?.run {
        // 'this' is the string
        uppercase().take(10)
    }
}

判断の枠組みとして:applyはオブジェクトの設定(レシーバーを返す)、letはnull安全な変換(ラムダの結果を返す)、alsoは副作用(レシーバーを返す)、runはレシーバーコンテキストでの計算結果を得るために使用する。

Null安全性とダブルバンの危険性

Null安全性は面接の基本的なトピックとして定着している。「なぜ!!は危険なのか」という質問は、候補者がKotlinの型システムを本質的に理解しているかを試すものである。

NullSafety.ktkotlin
fun findUser(id: String): User? {
    // Returns null if user not found
    return database.queryUser(id)
}

// Bad: !! defeats the purpose of null safety
fun riskyApproach(id: String) {
    val user = findUser(id)!! // NullPointerException if null
    println(user.username)
}

// Good: handle null explicitly
fun safeApproach(id: String) {
    val user = findUser(id) ?: run {
        println("User not found")
        return
    }
    println(user.username)
}

// Good: provide default with Elvis operator
fun displayName(id: String): String {
    return findUser(id)?.username ?: "Anonymous"
}

完全な回答では、!!はテストコードやプラットフォーム相互運用の境界など、nullが絶対に発生しない場面でのみ使用すべきであると言及する。

今すぐ練習を始めましょう!

面接シミュレーターと技術テストで知識をテストしましょう。

まとめ

  • Kotlin 2.3.20の名前ベースの分割代入は、位置依存のバグを排除する。3つのコンパイラモード(only-syntaxname-mismatchcomplete)を理解しておくことが重要である
  • KMPは2026年に本番運用レベルで安定している。expect/actual、共有モジュールアーキテクチャ、Swift Exportの相互運用に関する質問が想定される
  • コンテキストパラメータはコンテキストレシーバーを置き換えた。2.3.20でのオーバーロード解決の変更は、頻出のひっかけ問題である
  • コルーチンとFlowはAndroid面接を支配している。StateFlowSharedFlowWhileSubscribed、構造化された並行性を習得すべきである
  • Sealedインターフェースはコンパイル時の網羅性チェックを伴うUI状態モデリングに適している。sealed classよりもsealed interfaceが推奨される
  • スコープ関数には明確な判断基準が必要である:レシーバーか引数か、戻り値かレシーバーか
  • Null安全性は妥協できない項目である。!!が型システムを損なう理由を説明し、代替手段を示すことが求められる

今すぐ練習を始めましょう!

面接シミュレーターと技術テストで知識をテストしましょう。

タグ

#kotlin
#android
#interview
#kotlin-multiplatform
#coroutines

共有

関連記事