Topik wawancara representatif

Wawancara Kotlin: Kapan Context Parameter Harus Mengungguli Dependency Injection Eksplisit?

CodingSulit
Tim Redaksi Offer.ccDipublikasikan Diperbarui

Pertanyaan

Jelaskan resolusi context-parameter Kotlin dan tentukan kapan fitur ini lebih baik daripada parameter biasa atau kontainer dependency-injection.

Perintah dan Konteks

Beberapa fungsi dalam sebuah layanan Kotlin memerlukan UserService, logger, atau konteks transaksi yang sama. Tim ingin mengurangi penyaluran parameter yang berulang (parameter plumbing) tanpa menyembunyikan dependensi dalam variabel global, dan harus bermigrasi secara bertahap dari context receiver ke context parameter Kotlin 2.2. Jelaskan semantik, perilaku pada call-site, penanganan ambiguitas, dan batasannya. Sasaran audiens adalah seorang insinyur Kotlin/JVM; tidak ada kerangka kerja DI yang diasumsikan.

Hal yang Dievaluasi Pewawancara

Jawaban yang kuat menyatakan bahwa context parameter adalah dependensi waktu kompilasi yang tersedia secara implisit—bukan kontainer runtime. Resolusi terjadi di call-site dengan menemukan tipe yang cocok dalam cakupan (scope) saat ini; beberapa nilai yang kompatibel pada tingkat yang sama bersifat ambigu. Kandidat membedakan context receiver dari context parameter, memahami batasan migrasi dan fitur, serta menjelaskan mengapa parameter biasa sering kali lebih jelas pada batas publik.

Klarifikasi yang Perlu Ditanyakan Terlebih Dahulu

  1. Apakah dependensi melintasi API modul atau pustaka publik? Jika ya, parameter eksplisit lebih mudah ditemukan, diuji, dan dipanggil dari bahasa lain.
  2. Apakah dependensi stabil di dalam DSL lokal atau operasi transaksi? Jika ya, context parameter dapat menghilangkan penyaluran parameter yang repetitif.
  3. Bisakah dua implementasi dari tipe yang sama berada dalam cakupan? Jika ya, rancang argumen konteks bernama atau tipe pembungkus semantik (semantic wrapper types).
  4. Apakah proyek sudah menggunakan context receiver? Jika ya, konfirmasikan flag compiler dan cakupan migrasi; kedua mode tidak dapat dicampur dalam satu modul.

Kerangka Jawaban 30 Detik

“Context parameter mendeklarasikan dependensi pada signature fungsi sementara cakupan call-site menyediakannya. Kompiler menyelesaikannya berdasarkan tipe; dua kecocokan pada tingkat yang sama adalah sebuah kesalahan. Fitur ini cocok untuk DSL lokal, transaksi, atau konteks logging dan menghindari penyaluran parameter yang berulang. Untuk API publik, panggilan lintas bahasa, atau dependensi yang sering diganti, saya tetap mempertahankan parameter eksplisit. Selama migrasi, saya mengaktifkan opsi compiler yang sesuai dan memeriksa panggilan receiver serta kasus tingkat kelas karena kedua fitur tersebut tidak dapat dipertukarkan begitu saja.”

Pembahasan Mendalam Langkah demi Langkah

Deklarasikan dependensi, lalu sediakan dalam sebuah cakupan:

kotlin
interface UserService {
  fun findUser(id: Int): String
}

context(users: UserService)
fun greeting(id: Int): String = "Hello ${users.findUser(id)}"

fun main() {
  val service = object : UserService {
    override fun findUser(id: Int) = "user-$id"
  }
  context(service) {
    println(greeting(7))
  }
}

Signature masih mendeklarasikan dependensi, sehingga pemanggil dapat melihatnya; context(service) menyediakan nilainya di call-site. Resolusi mencocokkan tipe dalam cakupan saat ini, bukan nama variabel. Jika serviceA dan serviceB dari tipe kompatibel yang sama hadir pada tingkat yang sama, kompilasi gagal alih-alih memilih salah satu secara diam-diam. Gunakan argumen konteks eksplisit atau tipe pembungkus semantik ketika kedua arti tersebut valid.

Batasan dengan parameter biasa berkaitan dengan kepadatan informasi. Jika sebuah DSL lokal memiliki banyak fungsi yang berbagi satu konteks yang tidak dapat diubah (immutable), context parameter menghilangkan penyaluran parameter yang berulang. Jika satu fungsi menggunakan dependensi hanya sekali, parameter biasa lebih jelas. API pustaka publik juga harus mempertimbangkan panggilan Java, kemudahan penemuan di IDE, dan dokumentasi yang dihasilkan; semakin jauh sumber implisit berpindah, semakin tinggi biaya pemeliharaannya.

Migrasi dari context receiver lebih dari sekadar penggantian kata kunci. JetBrains mencatat bahwa context parameter memerlukan nama dan bahwa context receiver tingkat kelas tidak memiliki padanan langsung. Sebuah modul tidak dapat mengaktifkan kedua mode tersebut sekaligus. Ganti opsi compiler per modul, lalu periksa call-site, callable reference, dan receiver tingkat kelas. Dokumentasi resmi juga mencantumkan pembatasan: konstruktor tidak dapat mendeklarasikan context parameter, dan properti konteks tidak boleh memiliki backing field atau penginisialisasi.

Parameter anonim menghindari nama lokal yang tidak terpakai tetapi tetap berpartisipasi dalam resolusi:

kotlin
context(_: UserService)
fun logGreeting(id: Int) {
  println(greeting(id))
}

Aturan keputusannya adalah: “Gunakan konteks untuk state bersama yang lokal dan stabil; gunakan parameter biasa atau DI eksplisit untuk dependensi lintas batas, penggantian eksplisit, atau banyak implementasi.” Jangan ubah context parameter menjadi service locator. Pengujian tetap menyediakan dependensi melalui cakupan call-site, dan objek luar tetap memiliki masa pakai (lifetime) dependensi tersebut.

Contoh Jawaban Berkualitas Tinggi

Context parameter adalah deklarasi dependensi implisit pada waktu kompilasi. Fungsi menyertakan UserService dalam signature-nya, sementara pemanggil menyediakan sebuah instance di dalam context(service); compiler mencari cakupan saat ini berdasarkan tipe dan melaporkan ambiguitas ketika dua nilai cocok. Saya akan menggunakannya untuk DSL lokal, transaksi, atau rantai logging yang berbagi konteks stabil. Untuk API publik, interoperabilitas Java, atau dependensi yang sering diganti, saya akan mempertahankan parameter eksplisit atau DI. Selama migrasi context-receiver, saya akan mengganti opsi compiler per modul, memeriksa nama, callable reference, dan receiver tingkat kelas, serta memperhitungkan pembatasan seperti konstruktor dan properti backing-field.

Kesalahan Umum

  • Kesalahan → menyebut context parameter sebagai kontainer DI runtime → Alasan gagal: compiler menyelesaikannya di call-site → Solusi: jelaskan cakupan, pencocokan tipe, dan ambiguitas waktu kompilasi.
  • Kesalahan → berasumsi nilai bertipe sama dipilih berdasarkan nama variabel → Alasan gagal: kecocokan pada tingkat yang sama bersifat ambigu → Solusi: gunakan argumen konteks eksplisit atau tipe pembungkus semantik.
  • Kesalahan → mengganti context receiver dengan context parameter secara mekanis → Alasan gagal: penamaan, kasus tingkat kelas, dan callable reference berbeda → Solusi: migrasikan per modul dan periksa setiap call-site.
  • Kesalahan → membuat setiap dependensi menjadi implisit → Alasan gagal: kemampuan penemuan pada batas publik menurun → Solusi: jaga agar konteks tetap lokal dan stabil, dan gunakan parameter biasa pada batas antarmuka.

Pertanyaan Lanjutan dan Tanggapan

Lanjutan 1: Bagaimana cara Anda mengganti dependensi konteks dalam pengujian?

Buat fake atau stub yang mengimplementasikan antarmuka yang sama, lalu jalankan subjek di dalam context(fake) { ... }. Jika pengujian sering mengganti implementasi, parameter eksplisit biasanya lebih jelas dan membuat dependensi terlihat dalam laporan pengujian.

Lanjutan 2: Bagaimana jika dua layanan bertipe sama harus hidup berdampingan?

Perkenalkan tipe pembungkus semantik seperti PrimaryUserService dan AuditUserService, atau teruskan argumen konteks eksplisit di call-site. Jangan bergantung pada urutan deklarasi karena resolusi tidak menggunakan urutan sebagai prioritas.

Lanjutan 3: Mengapa konstruktor tidak dapat mendeklarasikan context parameter?

Dokumentasi Kotlin saat ini secara eksplisit membatasi konstruktor untuk mendeklarasikan context parameter. Untuk dependensi tingkat objek, gunakan parameter konstruktor biasa atau factory eksplisit; memaksakan konteks global akan menyembunyikan batas masa pakai dan thread.

Sumber publik

Pertanyaan terkait

Alat wawancara terkait

Gunakan Tangkapan Layar untuk perintah coding

Ambil tangkapan layar soal, lalu telusuri batasan, solusi, kode, edge case, dan kompleksitas secara berurutan.

Lihat alat