File .kts adalah wujud nyata Kotlin Scripting yang paling sering dipakai. Berbeda dari file .kt biasa yang wajib memiliki fun main() di dalam sebuah class, file .kts menerima pernyataan top-level langsung tanpa pembungkus. Hal ini membuat skrip terasa ringkas seperti Python atau Bash, namun tetap memiliki type-safety penuh Kotlin. Pada bab ini kita akan membedah empat unsur anatomi file .kts: pernyataan top-level, akses args, function lokal, dan shebang untuk pemanggilan langsung.

Top-Level Statement #

Bagian terbesar file .kts berisi pernyataan top-level: deklarasi variabel, pemanggilan function, dan kontrol alur. Saat kompilator memproses skrip, semua pernyataan ini dikumpulkan menjadi body sebuah class generated yang memiliki entry point khusus.

kotlin halo.kts — contoh minimal
val nama = "Budi"
val tahun = 2026
println("Halo, $nama! Tahun $tahun.")

Tidak ada fun main(), tidak ada class, dan tidak ada package deklarasi. Tiga baris tersebut sudah cukup untuk dijalankan dengan kotlinc -script halo.kts.

Akses args dari Command Line #

File .kts dapat menerima argumen dari command line lewat variabel khusus args. Variabel ini tersedia otomatis di seluruh skrip dan bertipe Array<String>. Perhatikan contoh skrip berikut yang menyapa pengguna berdasarkan argumen pertama.

kotlin sapa.kts — menerima args
// Pemakaian: kotlinc -script sapa.kts Budi 2026
when (args.size) {
    0 -> println("Penggunaan: sapa.kts <nama> <tahun>")
    1 -> println("Halo, ${args[0]}!")
    else -> {
        val nama = args[0]
        val tahun = args[1].toIntOrNull() ?: 2026
        println("Halo, $nama! Tahun $tahun.")
    }
}

Array args diindeks dari 0. Selalu validasi args.size sebelum mengakses indeks tertentu untuk menghindari ArrayIndexOutOfBoundsException.

Simulasi Anatomi Skrip #

Karena file .kts tidak dapat dikompilasi langsung dengan kotlinc biasa (memerlukan kotlinc -script), cuplikan berikut mensimulasikan struktur skrip sebagai file .kt standar yang merefleksikan anatomi sebenarnya:

kotlin
// Demo anatomi file .kts: top-level statements, args, dan shebang.
// File .kt ini mensimulasikan apa yang dijalankan kompilator saat
// membaca file .kts dengan parameter argumen.

import kotlin.math.PI
import kotlin.math.roundToInt

fun main(args: Array<String>) {
    println("=== Anatomi File .kts ===")
    println()

    // Bagian 1: pernyataan top-level tanpa pembungkus class/function.
    val pesan = "Halo dari skrip Kotlin"
    val pengguna = "Budi"
    println("Bagian 1 — Top-level statement:")
    println("  pesan = $pesan")
    println("  pengguna = $pengguna")
    println()

    // Bagian 2: akses args dari command line.
    // Saat menjalankan .kts dengan `kotlinc -script halo.kts Budi 2026`,
    // argumen "Budi" dan "2026" tersedia dalam array args.
    println("Bagian 2 — Akses args (command-line argument):")
    if (args.isEmpty()) {
        println("  Tidak ada argumen. Jalankan: kotlinc -script halo.kts Budi 2026")
        println("  Memakai nilai default: pengguna=Budi, tahun=2026")
    } else {
        println("  Jumlah argumen: ${args.size}")
        args.forEachIndexed { i, a ->
            println("  args[$i] = $a")
        }
    }
    println()

    // Bagian 3: definisi function lokal tetap didukung di .kts.
    fun sapa(nama: String, tahun: Int): String =
        "Halo, $nama! Selamat datang di tahun $tahun."

    println("Bagian 3 — Function lokal:")
    println("  ${sapa("Siti", 2026)}")
    println()

    // Bagian 4: import dan standard library tetap berfungsi.
    val piBulat = (PI * 100).roundToInt() / 100.0
    println("Bagian 4 — Standard library:")
    println("  PI dibulatkan 2 desimal = $piBulat")
}

Output yang Dihasilkan:

text
=== Anatomi File .kts ===

Bagian 1 — Top-level statement:
  pesan = Halo dari skrip Kotlin
  pengguna = Budi

Bagian 2 — Akses args (command-line argument):
  Tidak ada argumen. Jalankan: kotlinc -script halo.kts Budi 2026
  Memakai nilai default: pengguna=Budi, tahun=2026

Bagian 3 — Function lokal:
  Halo, Siti! Selamat datang di tahun 2026.

Bagian 4 — Standard library:
  PI dibulatkan 2 desimal = 3.14

Mari kita amati setiap bagian dari kode di atas:

  1. Bagian 1 (top-level statement): variabel pesan dan pengguna dideklarasikan langsung di body main(). Pada file .kts asli, baris-baris ini akan berada di root file tanpa pembungkus function.
  2. Bagian 2 (akses args): blok if (args.isEmpty()) memvalidasi argumen sebelum dipakai. Validasi ini wajib karena skrip bisa dipanggil tanpa argumen sama sekali.
  3. Bagian 3 (function lokal): function sapa dideklarasikan di dalam body main(). Pada file .kts, function top-level tetap didukung dan dapat ditempatkan di mana saja sebelum pemanggilan.
  4. Bagian 4 (standard library): kotlin.math.PI dan roundToInt() diimpor di awal file. Import harus selalu berada di bagian atas file .kts, sama seperti file .kt biasa.

Shebang untuk Pemanggilan Langsung #

Sistem operasi Unix mendukung shebang #! di baris pertama file untuk menentukan interpreter. File .kts dapat memakai shebang #!/usr/bin/env kotlinc -script sehingga dapat dipanggil langsung tanpa mengetik kotlinc -script di depan.

bash Skrip dengan shebang
#!/usr/bin/env kotlinc -script

println("Skrip dipanggil langsung")
println("Argument count: ${args.size}")

Setelah file disimpan (misalnya halo.kts), beri permission eksekusi dan panggil langsung:

bash
chmod +x halo.kts
./halo.kts Budi 2026

Shebang baris pertama akan diabaikan oleh kompilator Kotlin sebagai komentar, namun dikenali oleh kernel untuk memanggil interpreter yang tepat.

Function Lokal dan Helper #

Skrip tidak terbatas pada pernyataan sekuensial. Kita dapat mendefinisikan function helper, extension function, dan bahkan class data di dalam file .kts. Function top-level harus dideklarasikan sebelum dipanggil (Kotlin Scripting tidak melakukan hoisting seperti JavaScript).

kotlin Skrip dengan helper function
fun hitungPajak(harga: Double, tarif: Double = 0.11): Double = harga * tarif

val daftar = listOf(
    "Buku" to 50000.0,
    "Pulpen" to 12000.0,
    "Tas" to 150000.0
)

daftar.forEach { (nama, harga) ->
    val pajak = hitungPajak(harga)
    println("$nama: harga=$harga, pajak=$pajak")
}

Function hitungPajak dideklarasikan lebih dulu, lalu dipakai di forEach. Bila urutan dibalik, kompilator akan melaporkan unresolved reference.

Referensi Lanjutan #

  • Kotlin Scripting — panduan resmi Kotlin untuk anatomi file .kts dan top-level statement
  • kotlin-main-kts — contoh skrip main-kts di GitHub untuk pemakaian shebang dan dependensi

Selanjutnya #

Anatomi .kts sudah jelas. Selanjutnya kita akan naik ke level lebih praktis: membangun CLI tool utuh dengan struktur command, sub-command, dan exit code. Materi tersebut ada di chapter Command-Line Utilities.