Seleksi Kondisi

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 #

TipeDidukung
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 #

Aspekswitch Statementswitch Expression
Menghasilkan nilai
Fall-through defaultYa (bahaya)Tidak ada
break diperlukanYaTidak
Blok kode kompleksLangsungGunakan yield
Versi JavaSemuaJava 14+
ExhaustivenessTidak diperiksaHarus 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:#fff

switch 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
};
Menggunakan switch dengan enum tanpa default adalah keunggulan type safety: jika kamu menambahkan nilai baru ke enum tapi lupa menambahkannya di switch, compiler langsung memberikan error. Dengan int atau String, penambahan nilai baru akan diam-diam masuk ke default dan 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:

SituasiPilihan Terbaik
1-2 kondisi sederhanaif-else
Kondisi bersarang banyakGuard clause + if
Satu variabel vs banyak nilai tetapswitch expression (Java 14+)
Enum atau sealed classswitch expression tanpa default
Nilai default dari kondisiTernary atau switch expression
Pencocokan tipe dan nilaiPattern matching switch (Java 21)
Fall-through disengajaswitch statement + komentar eksplisit

Ringkasan #

  • Selalu gunakan kurung kurawal pada if meskipun hanya satu statement; tanpa kurung kurawal adalah sumber bug klasik yang sulit ditemukan.
  • Guard clause mengurangi nesting — balik kondisi dan return lebih awal untuk kasus-kasus khusus; logika utama tetap di level teratas dan mudah dibaca.
  • switch expression (Java 14+) adalah default modern — sintaks -> menghilangkan fall-through dan break; menghasilkan nilai langsung sehingga lebih fungsional.
  • Fall-through switch statement adalah sumber bug — selalu tambahkan break, atau tambahkan komentar // fall-through jika memang disengaja untuk berbagi logika antar case.
  • switch dengan enum tanpa default — compiler memastikan semua nilai enum di-handle; penambahan nilai enum baru langsung terdeteksi sebagai compile error.
  • Pattern matching switch (Java 21) — menggantikan rangkaian instanceof + cast; mendukung guarded pattern dengan when untuk kondisi tambahan.
  • yield di switch expression — gunakan yield nilai untuk mengembalikan nilai dari blok kode dalam switch expression, bukan return.
  • switch tidak mendukung long, double, float, boolean — hanya integer kecil, String, dan enum; gunakan if-else untuk tipe lain.

← Sebelumnya: Operator   Berikutnya: Perulangan →

About | Author | Content Scope | Editorial Policy | Privacy Policy | Disclaimer | Contact