switch klasik pada chapter sebelumnya adalah statement: struktur tersebut menjalankan aksi dan tidak menghasilkan apa pun, sehingga kita tetap mendeklarasikan variabel kosong lalu mengisinya cabang demi cabang. Praktik umum justru sebaliknya — mayoritas switch dipakai untuk memetakan nilai ke nilai lain, dan bentuk modern switch expression (final sejak Java 14, JEP 361) dirancang persis untuk itu. Bentuk baru ini menghasilkan nilai yang langsung diisikan ke variabel, memakai arrow label -> yang tidak mengenal fall-through, serta memberlakukan pemeriksaan exhaustiveness: compiler menolak switch yang belum menutup semua kemungkinan nilai. Chapter ini mengulas ketiga sifat itu, lalu menutup dengan pattern matching yang menjadikan switch alat pemeriksaan tipe yang ringkas.
Arrow Label dan Nilai Kembalian #
Contoh klasiknya adalah jumlah hari per bulan. Kita definisikan enum Bulan — tipe dengan dua belas konstanta tetap — lalu memetakan setiap bulan ke jumlah hari lewat arrow label:
public class JumlahHari {
enum Bulan { JANUARI, FEBRUARI, MARET, APRIL, MEI, JUNI,
JULI, AGUSTUS, SEPTEMBER, OKTOBER, NOVEMBER, DESEMBER }
public static void main(String[] args) {
Bulan bulan = Bulan.FEBRUARI;
// Tanpa default: compiler memastikan seluruh konstanta enum tercakup
int jumlahHari = switch (bulan) {
case JANUARI, MARET, MEI, JULI, AGUSTUS, OKTOBER, DESEMBER -> 31;
case APRIL, JUNI, SEPTEMBER, NOVEMBER -> 30;
case FEBRUARI -> 28; // tahun kabisat tidak diperhitungkan
};
System.out.println("Bulan : " + bulan);
System.out.println("Jumlah hari : " + jumlahHari);
}
}Output yang Dihasilkan:
Bulan : FEBRUARI
Jumlah hari : 28Mari kita bedah elemen-elemen barunya:
- Baris 7 (
int jumlahHari = switch (bulan) { ... };): seluruhswitchadalah ekspresi bertipeint— nilainya diambil dari cabang yang dijalankan, lalu diisikan ke variabeljumlahHariseperti layaknyaint x = 5;. Tanda titik koma penutup kini wajib ada di akhir. - Baris 9-11 (
case JANUARI, MARET, ... -> 31;): arrow label memetakan banyak nilai ke satu hasil dengan pemisah koma — idiom penumpukan labelcase 1:case 2:versi lama kini cukup satu baris. - Tanpa
default: seluruh dua belas konstantaBulantercakup, sehingga cabang penampung tidak diperlukan. Justru di sinilah exhaustiveness bekerja, seperti kita buktikan sebentar lagi. - Tanpa
break: arrow label tidak pernah fall-through — cabang selesai setelah menghasilkan satu nilai, dan bugbreakyang terlewat menjadi tidak mungkin ditulis.
yield untuk Badan Cabang Kompleks #
Nilai di kanan arrow kadang perlu dihitung lebih dulu dengan beberapa langkah. Badan cabang dapat berupa blok kurung kurawal, dan hasilnya dikembalikan dengan kata kunci yield:
public class YieldDiskon {
public static void main(String[] args) {
String tingkat = "GOLD";
int hargaTiket = 250000;
int biaya = switch (tingkat) {
case "REGULER" -> hargaTiket;
case "SILVER" -> {
int potongan = hargaTiket * 10 / 100;
yield hargaTiket - potongan;
}
case "GOLD" -> {
int potongan = hargaTiket * 25 / 100;
yield hargaTiket - potongan;
}
default -> hargaTiket;
};
System.out.println("Tingkat keanggotaan : " + tingkat);
System.out.println("Harga tiket : Rp" + hargaTiket);
System.out.println("Biaya setelah diskon: Rp" + biaya);
}
}Output yang Dihasilkan:
Tingkat keanggotaan : GOLD
Harga tiket : Rp250000
Biaya setelah diskon: Rp187500Cabang "GOLD" menghitung potongan 25% di dalam blok: 250000 * 25 / 100 menghasilkan 62500, lalu yield hargaTiket - potongan menyerahkan 187500 sebagai nilai seluruh switch expression. Kata kunci yield hanya sah di dalam blok switch expression — memakainya di tempat lain ditolak compiler — dan berperan seperti return yang khusus untuk cabang switch. Selector String juga menuntut default karena compiler tidak mungkin membuktikan semua String yang mungkin telah tercakup; ruang nilai teks memang tak terbatas.
Exhaustiveness sebagai Kontrak #
Pemeriksaan exhaustiveness mengubah lupa-menutup-cabang dari bug diam-diam menjadi error kompilasi yang tegas. Hilangkan satu konstanta dari JumlahHari — misalnya baris case FEBRUARI — dan compiler javac menolak dengan pesan persis:
KurangCase.java:6: error: the switch expression does not cover all possible input values
String nama = switch (warna) {
^
1 errorPerlindungan ini paling terasa saat proyek berkembang: menambahkan konstanta baru pada enum (misalnya WARNABARU pada tipe warna) langsung memaksa setiap switch expression atas enum itu diperbarui — compiler menunjuk baris-baris yang tertinggal, bukan pengujian manual di produksi. Versi statement klasik tidak menikmati jaminan ini; kehilangan cabang hanya menghasilkan keheningan saat default dijalankan.
Pattern Matching untuk switch #
Sejak Java 21 (JEP 441), label case dapat berupa pola tipe: selector dibandingkan bukan dengan konstanta, melainkan dengan bentuk datanya. Gabungkan dengan record pattern (JEP 440) dan label case null, switch menjadi pemeriksa objek yang ringkas:
public class PolaSwitch {
record Pesan(String pengirim, String isi) {}
static String deskripsi(Object o) {
return switch (o) {
case null -> "nilai null";
case Integer i when i > 100 -> "bilangan besar: " + i;
case Integer i -> "bilangan bulat: " + i;
case String s -> "teks sepanjang " + s.length() + " karakter";
case Pesan(String pengirim, String isi) ->
"pesan dari " + pengirim + " tentang \"" + isi + "\"";
default -> "tipe lain: " + o.getClass().getSimpleName();
};
}
public static void main(String[] args) {
Object bilanganKecil = 42;
Object bilanganBesar = 250;
Object teks = "Selamat pagi";
Object kosong = null;
Object pesan = new Pesan("Budi", "rapat jam 9");
Object pecahan = 3.5;
System.out.println(deskripsi(bilanganKecil));
System.out.println(deskripsi(bilanganBesar));
System.out.println(deskripsi(teks));
System.out.println(deskripsi(kosong));
System.out.println(deskripsi(pesan));
System.out.println(deskripsi(pecahan));
}
}Output yang Dihasilkan:
bilangan bulat: 42
bilangan besar: 250
teks sepanjang 12 karakter
nilai null
pesan dari Budi tentang "rapat jam 9"
tipe lain: DoubleEmpat mekanisme bekerja bergandengan dalam method deskripsi:
- Baris 4 (
case null): switch klasik melemparNullPointerExceptionuntuk selectornull; dengan label pola, nilainulldapat ditangani sebagai cabang biasa — pada contoh inikosongmenghasilkan “nilai null”. - Baris 5 (
case Integer i when i > 100): pola tipeInteger imencocokkan objek bertipeIntegersekaligus mengikat nilainya ke variabeli; klausulwhen(guarded pattern) menambah syarat lanjutan sehingga250masuk cabang “bilangan besar” sedangkan42jatuh ke polaIntegerberikutnya. Urutan label tetap diuji dari atas ke bawah. - Baris 7 (
case Pesan(String pengirim, String isi)): record pattern membongkar recordPesanlangsung di label —pengirimdanisiterikat komponen rekaman tanpa pemanggilan method aksesor satu per satu. Record sendiri adalah pembawa data tetap yang akan kita bahas tuntas pada Section Object-Oriented Programming. - Baris 8 (
default): selector bertipeObjectterbuka terhadap semua tipe, sehinggadefaultwajib menutup sisa kemungkinan —pecahanyang bertipeDoublemendarat di sini.
Kita kini memiliki dua wajah switch: statement untuk aksi, expression untuk nilai. Alur program sejauh ini masih berjalan maju sekali lewat tiap cabang. Berikutnya kita memasuki wilayah perulangan — for loop menjadi pembuka: struktur yang menampung inisialisasi, kondisi, dan kemajuan dalam satu baris ringkas.
Referensi #
- JEP 361: Switch Expressions
- JEP 441: Pattern Matching for switch
- JEP 440: Record Patterns
- JLS SE25 §14.11: The switch Statement and Expression
Berkas Sumber
Berkas pendukung halaman ini dapat diunduh dan dijalankan langsung sesuai penjelasan di atas.
JumlahHari.java
Unduh berkaspublic class JumlahHari {
enum Bulan { JANUARI, FEBRUARI, MARET, APRIL, MEI, JUNI,
JULI, AGUSTUS, SEPTEMBER, OKTOBER, NOVEMBER, DESEMBER }
public static void main(String[] args) {
Bulan bulan = Bulan.FEBRUARI;
// Tanpa default: compiler memastikan seluruh konstanta enum tercakup
int jumlahHari = switch (bulan) {
case JANUARI, MARET, MEI, JULI, AGUSTUS, OKTOBER, DESEMBER -> 31;
case APRIL, JUNI, SEPTEMBER, NOVEMBER -> 30;
case FEBRUARI -> 28; // tahun kabisat tidak diperhitungkan
};
System.out.println("Bulan : " + bulan);
System.out.println("Jumlah hari : " + jumlahHari);
}
}
PolaSwitch.java
Unduh berkaspublic class PolaSwitch {
record Pesan(String pengirim, String isi) {}
static String deskripsi(Object o) {
return switch (o) {
case null -> "nilai null";
case Integer i when i > 100 -> "bilangan besar: " + i;
case Integer i -> "bilangan bulat: " + i;
case String s -> "teks sepanjang " + s.length() + " karakter";
case Pesan(String pengirim, String isi) ->
"pesan dari " + pengirim + " tentang \"" + isi + "\"";
default -> "tipe lain: " + o.getClass().getSimpleName();
};
}
public static void main(String[] args) {
Object bilanganKecil = 42;
Object bilanganBesar = 250;
Object teks = "Selamat pagi";
Object kosong = null;
Object pesan = new Pesan("Budi", "rapat jam 9");
Object pecahan = 3.5;
System.out.println(deskripsi(bilanganKecil));
System.out.println(deskripsi(bilanganBesar));
System.out.println(deskripsi(teks));
System.out.println(deskripsi(kosong));
System.out.println(deskripsi(pesan));
System.out.println(deskripsi(pecahan));
}
}
YieldDiskon.java
Unduh berkaspublic class YieldDiskon {
public static void main(String[] args) {
String tingkat = "GOLD";
int hargaTiket = 250000;
int biaya = switch (tingkat) {
case "REGULER" -> hargaTiket;
case "SILVER" -> {
int potongan = hargaTiket * 10 / 100;
yield hargaTiket - potongan;
}
case "GOLD" -> {
int potongan = hargaTiket * 25 / 100;
yield hargaTiket - potongan;
}
default -> hargaTiket;
};
System.out.println("Tingkat keanggotaan : " + tingkat);
System.out.println("Harga tiket : Rp" + hargaTiket);
System.out.println("Biaya setelah diskon: Rp" + biaya);
}
}