Seleksi Kondisi #
Seleksi kondisi adalah mekanisme untuk mengarahkan alur eksekusi program berdasarkan kondisi tertentu. Java menyediakan beberapa konstruksi untuk ini, dan pilihan yang tepat mempengaruhi keterbacaan dan keamanan kode. Yang menarik: Java terus mengembangkan konstruksi seleksi kondisinya — switch yang awalnya hanya statement kini bisa menjadi ekspresi (Java 14), dan bisa digunakan untuk pattern matching tipe (Java 21). Artikel ini membahas semua konstruksi dari if-else klasik hingga switch modern, termasuk anti-pattern yang sering menyebabkan bug seperti fall-through tak disengaja dan kondisi yang terlalu bersarang.
if dan if-else #
if mengeksekusi blok kode hanya ketika kondisi bernilai true. Kondisi harus berupa ekspresi bertipe boolean — tidak ada implicit conversion dari int atau objek ke boolean seperti di C atau Python.
int stok = 5;
// if tunggal
if (stok == 0) {
System.out.println("Stok habis");
}
// if-else — dua cabang
if (stok > 0) {
System.out.println("Tersedia: " + stok);
} else {
System.out.println("Stok habis");
}
// if-else if-else — banyak cabang
int nilai = 78;
if (nilai >= 90) {
System.out.println("A");
} else if (nilai >= 80) {
System.out.println("B");
} else if (nilai >= 70) {
System.out.println("C");
} else if (nilai >= 60) {
System.out.println("D");
} else {
System.out.println("E");
}
flowchart TD
A([Mulai]) --> B{nilai >= 90?}
B -- Ya --> C[Cetak A]
B -- Tidak --> D{nilai >= 80?}
D -- Ya --> E[Cetak B]
D -- Tidak --> F{nilai >= 70?}
F -- Ya --> G[Cetak C]
F -- Tidak --> H{nilai >= 60?}
H -- Ya --> I[Cetak D]
H -- Tidak --> J[Cetak E]
C & E & G & I & J --> K([Selesai])Jebakan: if Tanpa Kurung Kurawal #
Java mengizinkan if tanpa kurung kurawal jika hanya ada satu statement, tapi ini sumber bug klasik:
// ANTI-PATTERN: if tanpa kurung kurawal
if (debug)
System.out.println("masuk sini");
System.out.println("ini SELALU dijalankan"); // ✗ bukan bagian dari if!
// Bug terkenal: Apple SSL/TLS "goto fail" terjadi karena pola ini
if (kondisi)
goto fail;
goto fail; // selalu dieksekusi!
// BENAR: selalu gunakan kurung kurawal
if (debug) {
System.out.println("masuk sini");
}
Guard Clause — Kurangi Nesting #
Kode yang terlalu banyak bersarang (deeply nested) sulit dibaca. Pola guard clause membalik kondisi: tangani kasus-kasus khusus lebih awal dan return, sehingga logika utama tetap di level teratas.
// ANTI-PATTERN: bersarang dalam — "arrow code"
public String prosesPembayaran(Pesanan pesanan) {
if (pesanan != null) {
if (pesanan.isValid()) {
if (pesanan.getSaldo() >= pesanan.getTotal()) {
if (!pesanan.isSudahDibayar()) {
// logika utama ada di dalam 4 level nesting
bayar(pesanan);
return "sukses";
} else {
return "sudah dibayar";
}
} else {
return "saldo tidak cukup";
}
} else {
return "pesanan tidak valid";
}
} else {
return "pesanan null";
}
}
// BENAR: guard clause — tangani kasus khusus lebih awal, return segera
public String prosesPembayaran(Pesanan pesanan) {
if (pesanan == null) return "pesanan null";
if (!pesanan.isValid()) return "pesanan tidak valid";
if (pesanan.isSudahDibayar()) return "sudah dibayar";
if (pesanan.getSaldo() < pesanan.getTotal()) return "saldo tidak cukup";
// logika utama di level atas, mudah dibaca
bayar(pesanan);
return "sukses";
}
switch Statement (Klasik) #
switch statement mencocokkan nilai sebuah ekspresi dengan serangkaian case. Lebih ringkas dari if-else if panjang ketika membandingkan satu variabel dengan banyak nilai tetap.
int hari = 3;
switch (hari) {
case 1:
System.out.println("Senin");
break;
case 2:
System.out.println("Selasa");
break;
case 3:
System.out.println("Rabu");
break;
case 4:
System.out.println("Kamis");
break;
case 5:
System.out.println("Jumat");
break;
default:
System.out.println("Akhir pekan");
}
Tipe yang Didukung switch Klasik #
| Tipe | Didukung |
|---|---|
byte, short, int, char | ✓ |
Byte, Short, Integer, Character (wrapper) | ✓ |
String | ✓ (sejak Java 7) |
enum | ✓ |
long, float, double, boolean | ✗ |
Fall-Through: Fitur atau Bug? #
Tanpa break, eksekusi “jatuh” ke case berikutnya (fall-through). Ini fitur yang sering tidak disengaja:
// ANTI-PATTERN: lupa break — fall-through tak disengaja
int kode = 2;
switch (kode) {
case 1:
System.out.println("satu");
case 2:
System.out.println("dua"); // dicetak
case 3:
System.out.println("tiga"); // JUGA dicetak karena lupa break!
default:
System.out.println("lain"); // JUGA dicetak!
}
// Output: dua, tiga, lain — padahal hanya "dua" yang diharapkan
// Fall-through yang DISENGAJA — gunakan komentar untuk memperjelas
switch (hari) {
case 1: // fall-through
case 2: // fall-through
case 3: // fall-through
case 4: // fall-through
case 5:
System.out.println("Hari kerja");
break;
case 6: // fall-through
case 7:
System.out.println("Akhir pekan");
break;
}
switch Expression (Java 14+) #
Java 14 memperkenalkan switch sebagai ekspresi yang menghasilkan nilai, dengan sintaks -> yang menghilangkan masalah fall-through dan break secara sekaligus. Ini adalah cara modern yang direkomendasikan.
// switch expression — menghasilkan nilai langsung
int hari = 3;
String namaHari = switch (hari) {
case 1 -> "Senin";
case 2 -> "Selasa";
case 3 -> "Rabu";
case 4 -> "Kamis";
case 5 -> "Jumat";
case 6 -> "Sabtu";
case 7 -> "Minggu";
default -> throw new IllegalArgumentException("Hari tidak valid: " + hari);
};
// Beberapa nilai dalam satu case
String jenisHari = switch (hari) {
case 1, 2, 3, 4, 5 -> "Hari kerja";
case 6, 7 -> "Akhir pekan";
default -> "Tidak valid";
};
// Gunakan yield untuk blok kode yang lebih kompleks
String kategori = switch (nilai) {
case 1, 2 -> "rendah";
case 3 -> {
System.out.println("nilai tengah diproses");
yield "sedang"; // yield menggantikan return di dalam switch expression
}
default -> "tinggi";
};
Perbandingan switch Statement vs switch Expression #
| Aspek | switch Statement | switch Expression |
|---|---|---|
| Menghasilkan nilai | ✗ | ✓ |
| Fall-through default | Ya (bahaya) | Tidak ada |
break diperlukan | Ya | Tidak |
| Blok kode kompleks | Langsung | Gunakan yield |
| Versi Java | Semua | Java 14+ |
| Exhaustiveness | Tidak diperiksa | Harus lengkap (enum/sealed) |
flowchart TD
A{Butuh\nmenghasilkan nilai?} -- Ya --> B["switch expression\ncase X -> nilai"]
A -- Tidak --> C{Banyak case\nberbagi logika\nfall-through sengaja?}
C -- Ya --> D["switch statement\ndengan komentar fall-through"]
C -- Tidak --> E{Berapa banyak\ncabang?}
E -- "2-3" --> F["if-else"]
E -- "4+" --> G["switch expression\natau switch statement"]
style B fill:#16a34a,color:#fff
style F fill:#3b82f6,color:#fff
style G fill:#16a34a,color:#fffswitch dengan String dan Enum #
switch bekerja baik dengan String (sejak Java 7) dan enum, dan keduanya lebih aman dibandingkan int:
// switch dengan String
String perintah = "start";
switch (perintah) {
case "start" -> System.out.println("Memulai...");
case "stop" -> System.out.println("Menghentikan...");
case "pause" -> System.out.println("Menjeda...");
default -> System.out.println("Perintah tidak dikenal: " + perintah);
}
// switch dengan enum — paling type-safe
enum StatusPesanan { MENUNGGU, DIPROSES, DIKIRIM, SELESAI, DIBATALKAN }
StatusPesanan status = StatusPesanan.DIKIRIM;
String pesan = switch (status) {
case MENUNGGU -> "Pesanan menunggu konfirmasi";
case DIPROSES -> "Pesanan sedang diproses";
case DIKIRIM -> "Pesanan dalam perjalanan";
case SELESAI -> "Pesanan telah diterima";
case DIBATALKAN -> "Pesanan dibatalkan";
// Tidak perlu default! Compiler memastikan semua enum di-handle
};
Menggunakanswitchdenganenumtanpadefaultadalah keunggulan type safety: jika kamu menambahkan nilai baru ke enum tapi lupa menambahkannya diswitch, compiler langsung memberikan error. DenganintatauString, penambahan nilai baru akan diam-diam masuk kedefaultdan mungkin tidak terdeteksi.
Pattern Matching switch (Java 21) #
Java 21 membawa switch ke level berikutnya: bisa mencocokkan tipe objek sekaligus melakukan binding — menggantikan rangkaian if (x instanceof Tipe t) yang panjang.
// Tanpa pattern matching — verbose dan repetitif
static String deskripsikan(Object obj) {
if (obj instanceof Integer i) {
return "Integer: " + i;
} else if (obj instanceof String s) {
return "String panjang " + s.length();
} else if (obj instanceof Double d) {
return "Double: " + d;
} else if (obj == null) {
return "null";
} else {
return "Tipe lain: " + obj.getClass().getSimpleName();
}
}
// Dengan pattern matching switch (Java 21) — ringkas dan aman
static String deskripsikan(Object obj) {
return switch (obj) {
case Integer i -> "Integer: " + i;
case String s -> "String panjang " + s.length();
case Double d -> "Double: " + d;
case null -> "null";
default -> "Tipe lain: " + obj.getClass().getSimpleName();
};
}
Guarded Pattern — Kondisi Tambahan dalam Case #
static String klasifikasi(Object obj) {
return switch (obj) {
case Integer i when i < 0 -> "Integer negatif: " + i;
case Integer i when i == 0 -> "Nol";
case Integer i -> "Integer positif: " + i;
case String s when s.isEmpty() -> "String kosong";
case String s -> "String: " + s;
case null -> "null";
default -> "Tipe lain";
};
}
// Contoh dengan sealed class — tidak butuh default
sealed interface Bentuk permits Lingkaran, Persegi, Segitiga {}
record Lingkaran(double r) implements Bentuk {}
record Persegi(double s) implements Bentuk {}
record Segitiga(double a, double t) implements Bentuk {}
static double hitungLuas(Bentuk b) {
return switch (b) {
case Lingkaran(double r) -> Math.PI * r * r;
case Persegi(double s) -> s * s;
case Segitiga(double a, double t) -> 0.5 * a * t;
// Tidak perlu default — sealed class exhaustive
};
}
Memilih Konstruksi yang Tepat #
Setiap konstruksi seleksi kondisi punya konteks idealnya:
| Situasi | Pilihan Terbaik |
|---|---|
| 1-2 kondisi sederhana | if-else |
| Kondisi bersarang banyak | Guard clause + if |
| Satu variabel vs banyak nilai tetap | switch expression (Java 14+) |
| Enum atau sealed class | switch expression tanpa default |
| Nilai default dari kondisi | Ternary atau switch expression |
| Pencocokan tipe dan nilai | Pattern matching switch (Java 21) |
| Fall-through disengaja | switch statement + komentar eksplisit |
Ringkasan #
- Selalu gunakan kurung kurawal pada
ifmeskipun hanya satu statement; tanpa kurung kurawal adalah sumber bug klasik yang sulit ditemukan.- Guard clause mengurangi nesting — balik kondisi dan
returnlebih awal untuk kasus-kasus khusus; logika utama tetap di level teratas dan mudah dibaca.switchexpression (Java 14+) adalah default modern — sintaks->menghilangkan fall-through danbreak; menghasilkan nilai langsung sehingga lebih fungsional.- Fall-through switch statement adalah sumber bug — selalu tambahkan
break, atau tambahkan komentar// fall-throughjika memang disengaja untuk berbagi logika antar case.switchdengan enum tanpadefault— compiler memastikan semua nilai enum di-handle; penambahan nilai enum baru langsung terdeteksi sebagai compile error.- Pattern matching
switch(Java 21) — menggantikan rangkaianinstanceof+ cast; mendukung guarded pattern denganwhenuntuk kondisi tambahan.yielddiswitchexpression — gunakanyield nilaiuntuk mengembalikan nilai dari blok kode dalamswitchexpression, bukanreturn.switchtidak mendukunglong,double,float,boolean— hanya integer kecil,String, danenum; gunakanif-elseuntuk tipe lain.