Saat proyek bertumbuh, satu file .kt tidak akan cukup. Kita perlu memecah kode menjadi beberapa file, masing-masing menangani satu area tanggung jawab. Pada chapter ini kita akan membahas bagaimana beberapa file Kotlin berbagi package yang sama dan saling memanggil deklarasi tanpa import eksplisit. Kita juga akan melihat konvensi penamaan file dan kapan memecah kode lebih baik daripada menjadikannya satu file besar.

Berbagi Package Lintas File #

Semua file yang mendeklarasikan package com.example.berkas berbagi ruang nama yang sama. Function, property, dan class top-level pada satu file dapat dipanggil langsung dari file lain di package yang sama tanpa statement import. Hal ini membuat pemecahan kode sangat alami: kita pisahkan kode berdasarkan tanggung jawab, bukan berdasarkan batasan teknis.

Struktur tipikal proyek multi-file:

text
src/main/kotlin/com/example/berkas/
├── Util.kt          ← object Util berisi helper format mata uang
├── Konstanta.kt     ← const NAMA_APLIKASI, const VERSI
├── Layanan.kt       ← class LayananPesanan, memakai Util dan Konstanta
└── Main.kt          ← fun main, titik masuk program

Contoh Kode #

Karena alat pengujian kita mengompilasi satu file pada satu waktu, kode berikut menyatukan keempat file tersebut menjadi satu berkas agar dapat dijalankan dan diamati outputnya. Setiap blok ditandai komentar yang menyatakan asal file:

kotlin
// File: multi_file_projects.kt
// Mendemonstrasikan konsep multi-file dalam satu paket.
// Catatan: program ini berdiri sendiri agar dapat dikompilasi dan dijalankan
// sebagai satu berkas. Pada proyek nyata, kode ini dipisah ke beberapa file .kt.
package com.example.berkas

// === Simulasi isi file: Util.kt ===
object Util {
    fun formatRupiah(nilai: Long): String = "Rp%,d".format(nilai)
}

// === Simulasi isi file: Konstanta.kt ===
const val NAMA_APLIKASI: String = "Toko Online"

// === Simulasi isi file: Layanan.kt ===
class LayananPesanan {
    private val daftar: MutableList<Pair<String, Long>> = mutableListOf()

    fun tambah(nama: String, harga: Long) {
        daftar.add(nama to harga)
    }

    fun ringkas(): String {
        val total = daftar.sumOf { it.second }
        val baris = daftar.joinToString(separator = "\n") { (nama, harga) ->
            "- $nama: ${Util.formatRupiah(harga)}"
        }
        return "Ringkasan:\n$baris\nTotal: ${Util.formatRupiah(total)}"
    }
}

// === Simulasi isi file: Main.kt ===
fun main() {
    println("Aplikasi: $NAMA_APLIKASI")
    val pesanan = LayananPesanan()
    pesanan.tambah("Buku Kotlin", 85_000)
    pesanan.tambah("Pulpen", 12_500)
    pesanan.tambah("Buku Catatan", 28_000)
    println(pesanan.ringkas())
}

Output yang Dihasilkan:

text
Aplikasi: Toko Online
Ringkasan:
- Buku Kotlin: Rp85,000
- Pulpen: Rp12,500
- Buku Catatan: Rp28,000
Total: Rp125,500

Mari kita amati bagian-bagian di atas:

  1. Baris 7-9 (object Util): helper untuk format mata uang. Pada proyek nyata, blok ini berada di Util.kt. Class Util dapat dipanggil dari Layanan.kt tanpa import karena berada di package yang sama.
  2. Baris 12 (const val NAMA_APLIKASI): konstanta global. Berada di Konstanta.kt; dipakai di Main.kt (baris 35) tanpa import.
  3. Baris 15-29 (class LayananPesanan): class service yang memakai Util.formatRupiah (baris 25 dan 28). Panggilan lintas file ini berjalan mulus karena keduanya berada pada package com.example.berkas.
  4. Baris 32-39 (fun main): titik masuk. Memakai NAMA_APLIKASI, membuat instance LayananPesanan, dan mencetak ringkasan. Pada proyek nyata, main ini berada di Main.kt.

Memecah File: Panduan Praktis #

Pemecahan file bukan sekadar soal estetika. File yang terlalu besar menjadi sulit dibaca dan dipelihara, sementara file yang terlalu kecil menyebabkan banyak lompatan saat membaca. Berikut panduan yang biasa dipakai:

  • Satu file idealnya 100-400 baris kode.
  • Satu file berisi satu tanggung jawab utama (misalnya: utilitas format, model data, service).
  • Extension function untuk satu tipe biasanya ditaruh pada satu file bernama <Tipe>Extensions.kt.
  • Konstanta global dikumpulkan di Konstanta.kt atau Constants.kt agar mudah dicari.

Kompilasi Bersama #

Kompilator Kotlin mengumpulkan semua file .kt dalam satu source set dan mengompilasinya bersamaan. Pada level JVM, semua function top-level dari package yang sama akan dikompilasi menjadi static method pada class-file berbeda (sesuai nama file Kotlin). Hal ini berarti kode byte tetap terorganisir per file walaupun pada level sumber semuanya terlihat menyatu dalam satu namespace.

Referensi Lanjutan #

  • Packages — konteks multi-file project dengan shared package di panduan resmi
  • Gradle — cara Gradle mengelola multi-file Kotlin source set

Selanjutnya #

Beberapa file yang dikompilasi bersamaan membentuk modul. Konsep modul penting karena memengaruhi visibility internal, yang akan kita bahas pada chapter Kotlin Modules.