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:

java JumlahHari.java
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:

text
Bulan       : FEBRUARI
Jumlah hari : 28

Mari kita bedah elemen-elemen barunya:

  1. Baris 7 (int jumlahHari = switch (bulan) { ... };): seluruh switch adalah ekspresi bertipe int — nilainya diambil dari cabang yang dijalankan, lalu diisikan ke variabel jumlahHari seperti layaknya int x = 5;. Tanda titik koma penutup kini wajib ada di akhir.
  2. Baris 9-11 (case JANUARI, MARET, ... -> 31;): arrow label memetakan banyak nilai ke satu hasil dengan pemisah koma — idiom penumpukan label case 1: case 2: versi lama kini cukup satu baris.
  3. Tanpa default: seluruh dua belas konstanta Bulan tercakup, sehingga cabang penampung tidak diperlukan. Justru di sinilah exhaustiveness bekerja, seperti kita buktikan sebentar lagi.
  4. Tanpa break: arrow label tidak pernah fall-through — cabang selesai setelah menghasilkan satu nilai, dan bug break yang 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:

java YieldDiskon.java
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:

text
Tingkat keanggotaan : GOLD
Harga tiket         : Rp250000
Biaya setelah diskon: Rp187500

Cabang "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:

text
KurangCase.java:6: error: the switch expression does not cover all possible input values
        String nama = switch (warna) {
                      ^
1 error

Perlindungan 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:

java PolaSwitch.java
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:

text
bilangan bulat: 42
bilangan besar: 250
teks sepanjang 12 karakter
nilai null
pesan dari Budi tentang "rapat jam 9"
tipe lain: Double

Empat mekanisme bekerja bergandengan dalam method deskripsi:

  1. Baris 4 (case null): switch klasik melempar NullPointerException untuk selector null; dengan label pola, nilai null dapat ditangani sebagai cabang biasa — pada contoh ini kosong menghasilkan “nilai null”.
  2. Baris 5 (case Integer i when i > 100): pola tipe Integer i mencocokkan objek bertipe Integer sekaligus mengikat nilainya ke variabel i; klausul when (guarded pattern) menambah syarat lanjutan sehingga 250 masuk cabang “bilangan besar” sedangkan 42 jatuh ke pola Integer berikutnya. Urutan label tetap diuji dari atas ke bawah.
  3. Baris 7 (case Pesan(String pengirim, String isi)): record pattern membongkar record Pesan langsung di label — pengirim dan isi terikat 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.
  4. Baris 8 (default): selector bertipe Object terbuka terhadap semua tipe, sehingga default wajib menutup sisa kemungkinan — pecahan yang bertipe Double mendarat 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 #

Berkas Sumber

Berkas pendukung halaman ini dapat diunduh dan dijalankan langsung sesuai penjelasan di atas.

JumlahHari.java

Unduh berkas
java
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);
    }
}

PolaSwitch.java

Unduh berkas
java
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));
    }
}

YieldDiskon.java

Unduh berkas
java
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);
    }
}