Salah satu fitur paling nyaman di Kotlin adalah string template — kemampuan menyisipkan nilai variabel atau hasil ekspresi langsung di dalam string literal tanpa operator + atau pemanggilan format. Cukup tulis $nama dan Kotlin akan menggantinya dengan isi variabel nama saat runtime. Untuk ekspresi yang lebih kompleks, gunakan ${ekspresi}. Chapter ini membongkar berbagai bentuk template — dari yang sederhana hingga template bersarang dan raw string multi-baris dengan """...""".

Template Sederhana dengan $ #

Bentuk paling dasar dari string template adalah $ diikuti nama variabel sederhana:

kotlin Contoh: template sederhana
val nama = "Budi"
val umur = 25
println("Halo, $nama")         // Halo, Budi
println("Umur: $umur tahun")   // Umur: 25 tahun

Saat kompilator melihat $nama di dalam string literal, kompilator menggantinya dengan pemanggilan nama.toString() di bytecode yang dihasilkan. Tidak ada pemrosesan runtime berat — string template dikompilasi menjadi StringBuilder.append() yang efisien.

Variabel yang bisa direferensikan harus sudah terlihat di scope. Nama variabel yang diawali huruf dan diikuti alphanumeric akan otomatis dipakai sebagai identifier. Jika kita ingin menulis tanda dolar literal (misalnya untuk teks mata uang), gunakan escape \$.

Template dengan Ekspresi $ #

Untuk ekspresi yang lebih kompleks dari sekadar nama variabel, gunakan ${ekspresi}:

kotlin Contoh: template dengan ekspresi
val panjang = 10
val lebar = 5
println("Luas: ${panjang * lebar}")     // Luas: 50

val daftar = listOf("apel", "jeruk", "mangga")
println("Buah pertama: ${daftar[0]}")    // Buah pertama: apel
println("Jumlah: ${daftar.size}")       // Jumlah: 3

val namaTeman = "Siti"
println("Halo, ${namaTeman.uppercase()}")  // Halo, SITI

Apapun yang berada di dalam ${...} dievaluasi sebagai ekspresi Kotlin. Kompilator memastikan tipe kembalian memiliki method toString() (yang berlaku untuk semua tipe via Any).

Contoh Lengkap dengan Template Bersarang #

kotlin
fun main() {
    val nama = "Budi"
    val umur = 25
    println("Halo, $nama")
    println("Umur: $umur tahun")

    val panjang = 10
    val lebar = 5
    println("Luas: ${panjang * lebar}")

    val daftar = listOf("apel", "jeruk", "mangga")
    println("Buah pertama: ${daftar[0]}")
    println("Jumlah: ${daftar.size}")

    val namaTeman = "Siti"
    println("Halo, ${namaTeman.uppercase()}")

    val prompt = "Nilai"
    println("${prompt}: ${"$nama berumur $umur"}")

    val harga = 15
    println("Harga: \$$harga ribu")

    val pesan = """
        |Pesan multi-baris:
        |Nama: $nama
        |Umur: $umur
        |Total: ${panjang + lebar}
    """.trimMargin()
    println(pesan)

    val json = """
        {
          "nama": "$nama",
          "umur": $umur,
          "aktif": true
        }
    """.trimIndent()
    println(json)
}

Output yang Dihasilkan:

text
Halo, Budi
Umur: 25 tahun
Luas: 50
Buah pertama: apel
Jumlah: 3
Halo, SITI
Nilai: Budi berumur 25
Harga: $15 ribu
Pesan multi-baris:
Nama: Budi
Umur: 25
Total: 15
{
  "nama": "Budi",
  "umur": 25,
  "aktif": true
}

Mari kita telaah baris-baris kode di atas:

  1. Baris 3–4 ("Halo, $nama"): tanda $ diikuti identifier nama akan diganti dengan nilai variabel nama. Karena nama bertipe String, tidak ada konversi tambahan.
  2. Baris 8 ("${panjang * lebar}"): ekspresi aritmetika panjang * lebar berada di dalam ${...}. Kotlin mengevaluasi ekspresi tersebut menjadi 50 lalu menyisipkannya ke string.
  3. Baris 11–12 (daftar[0], daftar.size): kita dapat mengakses elemen array dan properti collection di dalam ${}. Hasilnya tetap dikonversi ke string.
  4. Baris 16 ("${namaTeman.uppercase()}"): pemanggilan method juga valid. Method uppercase() mengembalikan String baru, lalu disisipkan ke template.
  5. Baris 19 ("${prompt}: ${"$nama berumur $umur"}"): ini adalah template bersarang (nested template). String dalam di dalam ${...} juga merupakan string template — $nama dan $umur di dalamnya dievaluasi terlebih dahulu, lalu hasilnya disisipkan ke template luar. Hasil akhirnya: Nilai: Budi berumur 25.
  6. Baris 22 ("Harga: \$$harga ribu"): escape \$ menghasilkan tanda dolar literal, lalu $harga dievaluasi sebagai template. Output: Harga: $15 ribu.
  7. Baris 25–31 ("""...""".trimMargin()): raw string multi-baris diapit tiga tanda kutip ganda """. Karakter | di awal setiap baris menjadi penanda margin yang dipotong oleh trimMargin(). Template $nama, $umur, dan ${panjang + lebar} tetap berlaku di dalam raw string.
  8. Baris 34–40 ("""...""".trimIndent()): raw string tanpa prefix |. Method trimIndent() mendeteksi indentasi minimum di seluruh baris dan memotongnya, sehingga blok JSON rapi tanpa indentasi berlebih.

Raw String dengan Triple Quote #

Raw string ("""...""") sangat berguna untuk teks yang mengandung banyak tanda kutip ganda atau yang membentang beberapa baris:

kotlin Contoh: raw string untuk JSON
val json = """
    {
      "nama": "Budi",
      "umur": 25
    }
""".trimIndent()
println(json)

Pada raw string, tanda kutip ganda tunggal tidak perlu di-escape. Karakter $ tetap berfungsi sebagai awalan template — gunakan \$ jika ingin tanda dolar literal.

trimMargin vs trimIndent #

Dua extension function pada String sering dipakai bersama raw string:

FunctionCara kerja
`trimMargin(marginPrefix: String = "“)`
trimIndent()Mendeteksi panjang indentasi minimum di seluruh baris non-kosong, lalu memotong indentasi sepanjang itu dari semua baris

trimMargin cocok ketika kita ingin penanda eksplisit untuk batas konten (lebih jelas secara visual). trimIndent lebih sederhana ketika indentasi sudah konsisten.

Escape vs Raw: Kapan Pakai Apa #

Tabel berikut merangkum perbedaan dua jenis string:

SifatEscaped string "..."Raw string """..."""
Multi-barisTidak (perlu \n)Ya
Escape \"WajibTidak perlu
Template $AktifAktif
Escape \$Untuk dolar literalUntuk dolar literal
Backslash \Diproses sebagai escapeLiteral

Referensi Lanjutan #

  • Kotlin Strings — dokumentasi resmi Kotlin Strings yang menjelaskan string template dengan $ dan ${}

Dengan pemahaman tentang string template, kita siap beranjak ke string formatting gaya C dengan String.format() — pendekatan yang lebih kaku tetapi memberi kontrol presisi terhadap lebar field dan jumlah desimal.