Teks yang menempati lebih dari satu baris, seperti data JSON, query SQL, atau templat HTML, selama ini ditulis dengan tanda \n dan penyambungan yang membuat sumbernya sulit dibaca. Java 15 memperkenalkan text block, yaitu sintaks string multi-baris yang menampilkan teks apa adanya di dalam tiga tanda kutip ganda. Topik ini membahas sintaksnya, aturan indentasi insidental (incidental whitespace) yang mengatur pemotongan spasi di depan, escape khusus seperti \""", dan pemakaiannya untuk JSON, SQL, serta teks terstruktur lainnya.
Persoalan string multi-baris#
Tanpa text block, string multi-baris dirakit baris demi baris dengan \n di dalam setiap bagian, ditambah penyambungan antarbaris. Setiap tanda kutip ganda di dalam teks juga harus di-escape agar tidak menutup string lebih awal.
public class JsonTanpaTextBlock {
public static void main(String[] args) {
String json = "{\n"
+ " \"nama\": \"Dewi\",\n"
+ " \"nilai\": 92\n"
+ "}";
System.out.println(json);
}
}{
"nama": "Dewi",
"nilai": 92
}Keluaran programnya benar, tetapi kode sumbernya tidak menyerupai keluaran itu sama sekali. Struktur JSON yang seharusnya terbaca sekilas malah tersembunyi. Tanda kutip harus di-escape, tanda \n wajib diulang di setiap baris, dan indentasi teks bercampur dengan indentasi kode program. Menambah satu field berarti menyalin pola yang rawan itu lagi, dan salah satu tanda \n saja langsung merusak formatnya.
Sintaks text block#
Text block dibuka dengan tiga tanda kutip ganda (""") yang diikuti pindah baris, lalu isi teks ditulis apa adanya pada baris-baris berikutnya. Penutupnya berupa """ yang boleh berdiri sendiri atau berdampingan dengan baris isi terakhir. Hasilnya tetap bertipe String, sehingga seluruh metode String tetap berlaku padanya.
public class JsonTextBlock {
public static void main(String[] args) {
String json = """
{
"nama": "Dewi",
"nilai": 92
}
""";
System.out.println(json);
}
}{
"nama": "Dewi",
"nilai": 92
}Sumbernya kini menyerupai keluarannya, tanpa satu pun tanda \n dan tanpa escape kutip tunggal untuk setiap tanda kutip. Tanda kutip ganda tunggal maupun berpasangan di dalam isi tidak perlu di-escape, karena pembatasnya adalah tiga tanda kutip berurutan. Baris yang mengikuti pembuka wajib ada, sehingga bentuk yang menaruh isi di baris yang sama dengan pembukanya ditolak kompilator.
// tidak-dijalankan
String salah = """ teks di baris yang sama """; // pembuka wajib diikuti pindah barisIndentasi insidental#
Text block membedakan dua jenis spasi di depan setiap baris, yaitu indentasi insidental yang hanya berfungsi merapikan kode sumber dan sengaja dipotong, serta sisa spasi setelah pemotongan yang menjadi bagian dari isi. Java menentukan indentasi insidental dengan mengambil indentasi terkecil dari seluruh baris isi dan baris yang memuat tanda penutup, dengan baris yang hanya berisi spasi tidak ikut dihitung. Posisi tanda penutup karena itu menentukan banyaknya spasi yang dipotong, dan menggesernya mengubah isi string.
public class UjiIndentasi {
public static void main(String[] args) {
String pertama = """
halo
dunia
""";
String kedua = """
halo
dunia
""";
System.out.println("[" + pertama + "]");
System.out.println("[" + kedua + "]");
System.out.println("panjang pertama: " + pertama.length());
System.out.println("panjang kedua : " + kedua.length());
}
}[halo
dunia
]
[ halo
dunia
]
panjang pertama: 11
panjang kedua : 19Kedua text block di atas berisi baris halo dan dunia pada kolom yang sama persis, tetapi tanda penutupnya berbeda posisi. Pada pertama, penutup berada sejajar dengan isinya, sehingga seluruh indentasi insidental terpotong dan hasilnya halo\ndunia\n dengan panjang 11. Pada kedua, penutup digeser empat spasi ke kiri, sehingga empat spasi terdalam berubah menjadi bagian isi yang sah dan hasilnya bertambah panjang menjadi 19. Kalimat penggabung seperti [ + pertama + ] pada contoh sengaja memakai kurung siku agar batas awal dan akhir stringnya terlihat jelas di keluaran.
Escape khusus dalam text block#
Text block mengenali escape \""" untuk menuliskan tiga tanda kutip ganda berurutan tanpa menutup bloknya. Kebutuhan ini muncul ketika isi teks memuat cuplikan program yang juga memakai text block, sebagaimana contoh dokumentasi berikut. Selain itu, tanda garis miring terbalik di akhir baris menghilangkan pindah baris sehingga dua baris sumber bergabung menjadi satu baris isi, dan escape \s menuliskan satu spasi yang dijamin tetap ada.
public class UjiEscapeTextBlock {
public static void main(String[] args) {
String dokumentasi = """
Bentuk umumnya:
String s = \"""
isi teks
\""";
""";
System.out.println(dokumentasi);
String satuBaris = """
baris pertama \
disambung tanpa pindah baris
""";
System.out.println(satuBaris);
}
}Bentuk umumnya:
String s = """
isi teks
""";
baris pertama disambung tanpa pindah barisBagian pertama membuktikan bahwa \""" menghasilkan tiga tanda kutip utuh di dalam isi, sehingga cuplikan kode bertingkat tetap bisa ditulis. Bagian kedua memperlihatkan penggabungan baris, karena garis miring terbalik di akhir baris pertama menghilangkan pindah baris dan menyambungkannya langsung dengan baris berikutnya.
Pemakaian untuk JSON, SQL, dan teks terstruktur#
Manfaat terbesar text block terasa pada templat teks terstruktur yang panjang, karena templatnya bisa dibiarkan berbentuk asli. Metode formatted yang tersedia sejak Java 15 melengkapinya, yaitu mengisi penanda %s dan %d di dalam templat dengan nilai yang diberikan, sehingga templat dan isinya terpisah dengan rapi.
public class KueriLaporan {
public static void main(String[] args) {
String tabel = "nilai_ujian";
int batas = 80;
String sql = """
SELECT nama, nilai
FROM %s
WHERE nilai >= %d
ORDER BY nilai DESC
""".formatted(tabel, batas);
System.out.println(sql);
}
}SELECT nama, nilai
FROM nilai_ujian
WHERE nilai >= 80
ORDER BY nilai DESCPola yang sama dipakai untuk menyusun dokumen JSON dengan field dinamis, badan surel dari templat, atau berkas konfigurasi kecil. Query yang menyertakan masukan pengguna sebaiknya dikirim lewat PreparedStatement dengan parameter terikat, sedangkan contoh di atas semata menunjukkan cara menyusun teks SQL-nya. Text block juga bisa diberi anotasi tipe seperti @Language pada IDE tertentu untuk mendapat penyorotan sintaks, meskipun hal itu di luar bahasa intinya.
Kesalahan umum#
- Menaruh isi pada baris yang sama dengan pembuka
""". Bentuk ini ditolak kompilator karena pembuka wajib diikuti pindah baris. - Mengira isi tidak berakhir dengan pindah baris. Selama penutup
"""berdiri sendiri, satu\nselalu ada di akhir isi, dan selisih ini cukup untuk menggagalkanequals. - Mengubah posisi tanda penutup tanpa mengecek ulang isi. Pergeseran penutup mengubah banyaknya spasi yang dipotong, sehingga string berubah senyap tanpa pesan error apa pun.
- Mencampur tab dan spasi di dalam satu text block. Perhitungan indentasi insidental bekerja per karakter spasi, sehingga tab yang tercampur menghasilkan pemotongan yang sulit diprediksi. Pegang satu konvensi, yaitu spasi, di dalam text block.
- Memakai text block untuk teks satu baris pendek. Literal biasa lebih ringkas, dan text block baru memberi keuntungan ketika barisnya memang lebih dari satu.
Ringkasan#
- Text block adalah string multi-baris dengan pembatas
""", hasilnya tetap bertipeStringdan isinya ditulis apa adanya tanpa\nantarbaris. - Indentasi insidental dihitung dari indentasi terkecil seluruh baris isi dan baris penutupnya, lalu dipotong dari setiap baris, sehingga posisi tanda penutup menentukan isi akhirnya.
- Tanda penutup yang berdiri sendiri meninggalkan satu pindah baris di akhir isi, dan escape
\""",\s, serta garis miring terbalik di akhir baris menutup kebutuhan khususnya. - Kombinasi text block dengan
formattedmenyediakan cara ringkas menyusun JSON, SQL, dan templat teks terstruktur lainnya.
Referensi#
- JEP 378: Text Blocks — OpenJDK — Proposal resmi text block, mencakup motivasi, aturan indentasi insidental, dan escape barunya.
- Chapter 3.10.6 Text Blocks — JLS Java SE 21 — Spesifikasi bahasa untuk pembatas, perhitungan indentasi, dan escape dalam text block.