Interaksi dengan pengguna tidak hanya soal mencetak output — program juga perlu menerima input. Kotlin menyediakan beberapa function untuk membaca standard input (stdin), dan pilihannya berkembang dari masa ke masa. Function readLine() yang lama telah dideprecate sejak Kotlin 1.6 karena memaksa kita menangani String? padahal mayoritas kasus mengharapkan string non-null. Penggantinya adalah readln() dan readlnOrNull(), yang memisahkan jalan happy path (lempar exception saat EOF) dan defensive path (kembalikan null saat EOF). Pada chapter ini kita akan membandingkan ketiganya dan melihat pola membaca banyak baris input.

readLine() dan Mengapa Dideprecate #

Function readLine() mengembalikan String? — null jika stream stdin sudah mencapai EOF (End Of File). Desain ini membuat kode pemula dipenuhi ? dan !! meskipun ketika berjalan di terminal interaktif, EOF jarang terjadi. Sejak Kotlin 1.6, function ini ditandai sebagai deprecated, dan kompilator akan memberi peringatan jika kita tetap memakainya:

kotlin Contoh: readLine() yang sudah deprecated
val nama = readLine()          // String?, peringatan deprecation
val umur = readLine()!!.toInt() // !! berbahaya pada EOF

Untuk menggantinya, Kotlin memperkenalkan dua function baru yang lebih jelas perannya. Pilih salah satu sesuai kebutuhan: kita ingin exception saat EOF, atau kita ingin menangani null secara eksplisit.

readln() dan readlnOrNull() #

FunctionTipe kembalianPerilaku saat EOF
readln()StringMelempar RuntimeException
readlnOrNull()String?Mengembalikan null
readLine() (deprecated)String?Mengembalikan null

readln() adalah pilihan idiomatis ketika program kita berjalan di terminal interaktif dan EOF dianggap kondisi tak terduga. Sebaliknya, readlnOrNull() cocok untuk program yang menerima input dari file atau pipe, di mana EOF adalah akhir data yang wajar.

Contoh Membaca Input #

kotlin
25
Bandung
Surabaya
42"}
fun main() {
    println("Siapa nama Anda?")
    val nama = readln()
    println("Halo, $nama!")

    println("Berapa umur Anda?")
    val umur = readlnOrNull()
    if (umur != null) {
        println("Tahun depan Anda akan berumur ${umur.toInt() + 1}")
    } else {
        println("Input tidak terbaca")
    }

    println("Sebutkan dua kota favorit (satu per baris):")
    val kota1 = readlnOrNull() ?: "Tanpa nama"
    val kota2 = readlnOrNull() ?: "Tanpa nama"
    println("Kota favorit: $kota1 dan $kota2")

    println("Masukkan angka untuk diparsing (atau tekan Ctrl+D untuk EOF):")
    val baris = readlnOrNull()
    val angka = baris?.toIntOrNull() ?: 0
    println("Angka yang dibaca: $angka")
}

Output yang Dihasilkan:

text
Siapa nama Anda?
Halo, Budi!
Berapa umur Anda?
Tahun depan Anda akan berumur 26
Sebutkan dua kota favorit (satu per baris):
Kota favorit: Bandung dan Surabaya
Masukkan angka untuk diparsing (atau tekan Ctrl+D untuk EOF):
Angka yang dibaca: 42

Mari kita telaah baris-baris kode di atas:

  1. Baris 2 (println("Siapa nama Anda?")): program mencetak prompt terlebih dahulu agar pengguna tahu input apa yang diharapkan. Pola prompt-then-read ini umum di program CLI interaktif.
  2. Baris 3 (val nama = readln()): function readln() membaca satu baris dari stdin, menghapus karakter newline di akhir, dan mengembalikan String non-null. Jika stdin sudah EOF (misalnya pengguna menekan Ctrl+D), readln() akan melempar exception.
  3. Baris 6 (val umur = readlnOrNull()): function readlnOrNull() mengembalikan String?. Pemanggilan ini lebih defensif — jika EOF terjadi, kita mendapat null, bukan exception.
  4. Baris 7 (if (umur != null)): setelah pengecekan null, smart cast Kotlin mengubah tipe umur menjadi String di dalam blok if, sehingga kita dapat memanggil umur.toInt() tanpa operator !!.
  5. Baris 13–14 (readlnOrNull() ?: "Tanpa nama"): operator elvis ?: memberikan nilai default "Tanpa nama" jika readlnOrNull() mengembalikan null. Pola ini sangat berguna saat membaca banyak baris yang mungkin saja terputus di tengah.
  6. Baris 18 (baris?.toIntOrNull() ?: 0): kombinasi safe call ?. dan elvis ?: untuk parsing aman. toIntOrNull() mengembalikan Int? (null jika teks bukan angka valid), lalu elvis memberi nilai default 0.

Membaca Banyak Baris #

Ketika program perlu membaca sejumlah baris, kita dapat menggunakan loop while yang berhenti ketika readlnOrNull() mengembalikan null:

kotlin Contoh: membaca sampai EOF
fun main() {
    var jumlah = 0
    var baris: String?
    while (true) {
        baris = readlnOrNull() ?: break
        jumlah++
        println("Baris ke-$jumlah: $baris")
    }
    println("Total baris dibaca: $jumlah")
}

Pola di atas adalah pola umum untuk Unix filter — program yang membaca stdin baris demi baris hingga EOF, melakukan pemrosesan, lalu menulis hasil ke stdout. Tool seperti cat, grep, dan wc bekerja dengan cara serupa.

Membaca Input dengan java.util.Scanner #

Untuk parsing yang lebih kompleks (membaca token per token, bukan baris per baris), Kotlin dapat memanfaatkan java.util.Scanner dari Java:

kotlin Contoh: java.util.Scanner
import java.util.Scanner

fun main() {
    val scanner = Scanner(System.`in`)
    print("Masukkan dua angka dipisah spasi: ")
    val a = scanner.nextInt()
    val b = scanner.nextInt()
    println("Hasil: ${a + b}")
}

Scanner memudahkan parsing tipe primitif langsung dari stdin tanpa perlu toInt() manual. Namun untuk membaca baris utuh, readln() tetap lebih ringkas dan idiomatis di Kotlin.

Referensi Lanjutan #

Dengan kemampuan membaca input baris-per-baris, kita siap membahas function readln() lebih dalam pada chapter berikutnya — termasuk cara mengaitkannya dengan parsing bertingkat seperti readln().toInt().toString() dan penanganan galatnya.