Operator

Operator #

Operator adalah simbol yang menginstruksikan compiler untuk melakukan operasi tertentu pada satu atau lebih nilai (operand). Java membagi operator ke dalam tujuh kategori berdasarkan fungsinya. Yang membedakan artikel ini dari sekadar daftar simbol: setiap kategori disertai dengan perilaku yang tidak intuitif dan anti-pattern yang sering menyebabkan bug — seperti integer division yang membuang desimal, perbedaan && vs &, dan jebakan operator ++ pre vs post increment. Memahami mengapa setiap operator berperilaku seperti itu jauh lebih berguna daripada sekadar tahu apa yang dilakukannya.

Operator Aritmatika #

Operator aritmatika melakukan operasi matematika dasar. Semua operator ini bekerja pada tipe numerik primitif (int, long, double, dll) dan menghasilkan nilai bertipe numerik.

OperatorNamaContohHasil
+Penjumlahan10 + 313
-Pengurangan10 - 37
*Perkalian10 * 330
/Pembagian10 / 33 (bukan 3.33!)
%Sisa bagi (modulo)10 % 31
+Konkatenasi String"a" + "b""ab"

Jebakan Integer Division #

Pembagian dua int menghasilkan int — bagian desimal dibuang, bukan dibulatkan. Ini adalah salah satu sumber bug yang paling sering tidak disadari developer baru:

// ANTI-PATTERN: pembagian integer membuang desimal
int a = 10, b = 3;
double hasil = a / b;            // 3.0 — bukan 3.333...!
System.out.println(a / b);       // 3 — desimal dibuang

double rata = (1 + 2 + 3) / 3;  // 2.0 — bukan 2.0!
// 1+2+3 = 6, lalu 6/3 = 2 (integer), baru dikonversi ke double

// BENAR: cast salah satu operan ke double dulu
double hasil = (double) a / b;          // 3.3333...
double hasil = a / (double) b;          // 3.3333...
double rata  = (1 + 2 + 3) / 3.0;      // 2.0 — benar
double rata  = (double)(1 + 2 + 3) / 3; // 2.0 — benar

Modulo pada Bilangan Negatif #

% di Java mengikuti tanda dividend (bilangan yang dibagi), bukan divisor:

System.out.println( 7 % 3);   //  1
System.out.println(-7 % 3);   // -1 — negatif! (bukan 2)
System.out.println( 7 % -3);  //  1

// Jika butuh hasil selalu positif (true modulo):
int hasil = ((7) % 3 + 3) % 3; // 2 — selalu non-negatif

Operator Increment dan Decrement #

++ dan -- punya dua bentuk dengan perbedaan penting:

int x = 5;

// Post-increment: nilai lama digunakan dulu, LALU di-increment
int a = x++;   // a = 5, kemudian x menjadi 6
System.out.println(a); // 5
System.out.println(x); // 6

// Pre-increment: di-increment dulu, LALU nilai baru digunakan
int b = ++x;   // x menjadi 7 dulu, kemudian b = 7
System.out.println(b); // 7
System.out.println(x); // 7

// ANTI-PATTERN: ++/-- dalam ekspresi kompleks — sulit dibaca
int y = 5;
int z = y++ + ++y; // membingungkan: y=5, lalu 5 + 7 = 12? atau?

// BENAR: gunakan ++/-- sebagai statement tersendiri
y++;
z = y + y;

Operator Penugasan #

Operator penugasan menyimpan nilai ke variabel. Semua operator compound assignment (+=, -=, dll) melakukan operasi sekaligus menyimpan hasilnya — dan secara implisit melakukan narrowing cast yang bisa mengejutkan.

OperatorSetara denganContoh
=x = 10
+=x = x + nx += 5
-=x = x - nx -= 3
*=x = x * nx *= 2
/=x = x / nx /= 4
%=x = x % nx %= 3
&=x = x & nx &= 0xFF
|=x = x | nx |= 0x01
^=x = x ^ nx ^= mask
<<=x = x << nx <<= 2
>>=x = x >> nx >>= 1

Compound Assignment dan Implicit Cast #

Operator compound assignment menyertakan narrowing cast implisit yang tidak dimiliki ekuivalennya secara manual:

byte b = 10;

// Ini COMPILE ERROR — int tidak bisa otomatis masuk ke byte
b = b + 1;          // ✗ ERROR: possible lossy conversion from int to byte

// Ini BENAR — += menyertakan cast implisit
b += 1;             // ✓ setara dengan b = (byte)(b + 1)

// Implikasi: compound assignment bisa menyebabkan narrowing diam-diam
byte nilai = 100;
nilai += 100;        // nilai = (byte)(100 + 100) = (byte)200 = -56! (overflow)

Operator Pembanding #

Operator pembanding membandingkan dua nilai dan selalu menghasilkan boolean. Digunakan sebagai kondisi di if, while, for, dan ekspresi boolean lainnya.

OperatorMaknaContohHasil
==Sama dengan5 == 5true
!=Tidak sama dengan5 != 3true
>Lebih besar5 > 3true
<Lebih kecil5 < 3false
>=Lebih besar atau sama5 >= 5true
<=Lebih kecil atau sama5 <= 4false

== pada tipe referensi membandingkan alamat, bukan nilai. Ini sudah dibahas di artikel Tipe Data, tapi layak diulang karena sangat sering menyebabkan bug:

String s1 = new String("halo");
String s2 = new String("halo");

s1 == s2;        // false — alamat memori berbeda
s1.equals(s2);   // true  — isi sama

// Khusus null-safe comparison, gunakan Objects.equals:
Objects.equals(s1, null); // false, tanpa NullPointerException
Objects.equals(null, s2); // false, aman

Operator Logika #

Operator logika menggabungkan dua ekspresi boolean menjadi satu hasil boolean. Ada dua versi: short-circuit (&&, ||) dan non-short-circuit (&, |).

OperatorNamaDeskripsi
&&AND (short-circuit)true jika kedua operan true; berhenti di kiri jika kiri false
||OR (short-circuit)true jika salah satu true; berhenti di kiri jika kiri true
!NOTMembalik nilai boolean
&AND (non-short-circuit)Evaluasi kedua sisi selalu
|OR (non-short-circuit)Evaluasi kedua sisi selalu
^XORtrue jika tepat satu operan true

Short-Circuit Evaluation #

Ini adalah perilaku penting yang sering dimanfaatkan di Java:

// && berhenti di operan kiri jika hasilnya sudah pasti false
String nama = null;
if (nama != null && nama.length() > 0) {  // ✓ aman
    System.out.println(nama);
    // jika nama null, nama.length() tidak pernah dipanggil
}

// ANTI-PATTERN: tanpa null check di kiri
if (nama.length() > 0 && nama != null) {  // ✗ NullPointerException jika nama null!

// || berhenti di operan kiri jika hasilnya sudah pasti true
int cache = -1;
int nilai = (cache != -1) || hitungNilaiMahal(); // hitungNilaiMahal() tidak dipanggil jika cache valid
flowchart LR
    subgraph "&& Short-Circuit"
        A["Evaluasi\noperan kiri"] --> B{Kiri == false?}
        B -- Ya --> C["Kembalikan false\nKanan TIDAK dievaluasi"]
        B -- Tidak --> D["Evaluasi\noperan kanan"] --> E["Kembalikan\nnilai kanan"]
    end
flowchart LR
    subgraph "|| Short-Circuit"
        A["Evaluasi\noperan kiri"] --> B{Kiri == true?}
        B -- Ya --> C["Kembalikan true\nKanan TIDAK dievaluasi"]
        B -- Tidak --> D["Evaluasi\noperan kanan"] --> E["Kembalikan\nnilai kanan"]
    end

&& vs & — Kapan Pakai Yang Mana #

// Gunakan && (short-circuit) untuk kondisi normal
// Operan kanan tidak dievaluasi jika tidak perlu
if (list != null && list.size() > 0) { }

// Gunakan & (non-short-circuit) HANYA jika operan kanan harus selalu dieksekusi
// karena punya efek samping yang dibutuhkan
if (validasiField1() & validasiField2()) {
    // kedua validasi selalu berjalan, error messages dari keduanya terkumpul
}

Operator Bitwise #

Operator bitwise bekerja langsung pada level bit integer. Paling umum digunakan untuk flag bitmask, operasi performa tinggi, atau saat berinteraksi dengan protokol biner.

OperatorNamaDeskripsi
&ANDBit 1 hanya jika kedua bit 1
|ORBit 1 jika salah satu bit 1
^XORBit 1 jika tepat satu bit 1
~NOT (komplemen)Membalik semua bit
<<Left shiftGeser bit ke kiri, isi 0 di kanan
>>Signed right shiftGeser ke kanan, pertahankan sign bit
>>>Unsigned right shiftGeser ke kanan, isi 0 di kiri
int a = 0b1010; // 10 dalam desimal
int b = 0b1100; // 12 dalam desimal

System.out.println(a & b);   // 0b1000 = 8  (AND)
System.out.println(a | b);   // 0b1110 = 14 (OR)
System.out.println(a ^ b);   // 0b0110 = 6  (XOR)
System.out.println(~a);      // -11          (NOT: flip semua bit + sign)

// Shift: perkalian/pembagian dengan pangkat 2 yang lebih cepat
int x = 4;
System.out.println(x << 2);  // 16  (x * 4)
System.out.println(x >> 1);  // 2   (x / 2)

// Bitmask — simpan banyak flag dalam satu int
final int FLAG_BACA   = 0b001; // 1
final int FLAG_TULIS  = 0b010; // 2
final int FLAG_EKSEKUSI = 0b100; // 4

int izin = FLAG_BACA | FLAG_TULIS; // izin = 0b011 = 3

// Cek apakah flag tertentu aktif
boolean bisaBaca  = (izin & FLAG_BACA)    != 0; // true
boolean bisaEksek = (izin & FLAG_EKSEKUSI) != 0; // false

// Tambah flag
izin |= FLAG_EKSEKUSI;   // izin = 0b111 = 7

// Hapus flag
izin &= ~FLAG_TULIS;     // izin = 0b101 = 5

Operator Ternary #

Operator ternary ? : adalah ekspresi kondisional tiga operan — satu-satunya operator Java dengan tiga bagian. Ia menghasilkan nilai (bukan statement), jadi bisa digunakan di mana pun ekspresi diharapkan.

// Sintaks: kondisi ? nilaiJikaTrue : nilaiJikaFalse
int maks = (a > b) ? a : b;

String status = (umur >= 18) ? "dewasa" : "anak-anak";

// Berguna untuk nilai default
String nama = (input != null) ? input : "Anonim";

// Atau gunakan ekuivalennya yang lebih bersih:
String nama = Objects.requireNonNullElse(input, "Anonim");

Ternary vs if-else — Kapan Pakai Yang Mana #

// BENAR: ternary untuk ekspresi sederhana satu baris
int abs = (x >= 0) ? x : -x;
String label = aktif ? "Aktif" : "Nonaktif";

// ANTI-PATTERN: ternary bersarang — sangat sulit dibaca
String kategori = (nilai >= 90) ? "A"
                : (nilai >= 80) ? "B"
                : (nilai >= 70) ? "C"
                : (nilai >= 60) ? "D" : "E"; // ✗ gunakan if-else atau switch

// BENAR: if-else untuk logika yang kompleks atau bersarang
String kategori;
if      (nilai >= 90) kategori = "A";
else if (nilai >= 80) kategori = "B";
else if (nilai >= 70) kategori = "C";
else if (nilai >= 60) kategori = "D";
else                  kategori = "E";

Operator instanceof #

instanceof memeriksa apakah sebuah objek adalah instance dari tipe tertentu. Hasilnya boolean. Sejak Java 16, ada pattern matching instanceof yang menggabungkan pemeriksaan tipe dan casting dalam satu langkah.

// Cara lama — periksa lalu cast secara terpisah
Object obj = "Halo Java";
if (obj instanceof String) {
    String s = (String) obj;  // cast manual
    System.out.println(s.length());
}

// Cara baru (Java 16+) — pattern matching: periksa + bind sekaligus
if (obj instanceof String s) {  // s otomatis bertipe String dalam blok ini
    System.out.println(s.length()); // tidak perlu cast manual
}

// Pattern matching membuat kode jauh lebih ringkas untuk polymorphism
void proses(Object bentuk) {
    if (bentuk instanceof Lingkaran c) {
        System.out.println("Jari-jari: " + c.getJariJari());
    } else if (bentuk instanceof Persegi p) {
        System.out.println("Sisi: " + p.getSisi());
    } else if (bentuk instanceof Segitiga s) {
        System.out.println("Alas: " + s.getAlas());
    }
}

// instanceof selalu false untuk null
String s = null;
System.out.println(s instanceof String); // false — tidak throw NPE

Precedence (Urutan Prioritas) #

Ketika beberapa operator muncul dalam satu ekspresi, Java mengevaluasinya berdasarkan precedence — urutan prioritas dari tinggi ke rendah:

PrioritasOperatorAsosiativitas
1 (tertinggi)++ -- (post), (), [], .Kiri ke kanan
2++ -- (pre), + - (unary), ~, !Kanan ke kiri
3* / %Kiri ke kanan
4+ -Kiri ke kanan
5<< >> >>>Kiri ke kanan
6< > <= >= instanceofKiri ke kanan
7== !=Kiri ke kanan
8&Kiri ke kanan
9^Kiri ke kanan
10|Kiri ke kanan
11&&Kiri ke kanan
12||Kiri ke kanan
13? : (ternary)Kanan ke kiri
14 (terendah)= += -= dllKanan ke kiri
// Contoh precedence dalam praktik
int hasil = 2 + 3 * 4;       // 14 — bukan 20: * lebih tinggi dari +
int hasil = (2 + 3) * 4;     // 20 — kurung memaksa urutan

boolean cek = 5 > 3 && 2 < 4; // true: > dan < dievaluasi dulu, baru &&

// ANTI-PATTERN: mengandalkan precedence untuk ekspresi kompleks
int x = a++ * b-- + c >> 2;  // ✗ sangat sulit dibaca

// BENAR: gunakan kurung untuk memperjelas urutan yang dimaksud
int x = ((a++) * (b--) + c) >> 2; // ✓ niat jelas

Ringkasan #

  • Integer division membuang desimal7 / 2 menghasilkan 3, bukan 3.5; cast ke double salah satu operan dulu: (double) 7 / 2 atau 7 / 2.0.
  • && dan || adalah short-circuit — operan kanan tidak dievaluasi jika hasil sudah pasti; manfaatkan ini untuk null-check: obj != null && obj.metode().
  • == pada referensi membandingkan alamat — gunakan .equals() untuk isi objek dan Objects.equals() untuk null-safe comparison.
  • Compound assignment menyertakan implicit castb += 1 tidak sama persis dengan b = b + 1 untuk byte/short; yang pertama menyertakan narrowing cast otomatis.
  • Pre vs post increment berbedaa++ menggunakan nilai lama lalu increment; ++a increment dulu lalu gunakan nilai baru; hindari keduanya dalam ekspresi kompleks.
  • instanceof pattern matching (Java 16+)if (obj instanceof String s) menggabungkan pemeriksaan tipe dan cast dalam satu langkah; instanceof selalu false untuk null.
  • Ternary untuk ekspresi sederhana sajakondisi ? a : b baik untuk nilai default atau pilihan satu baris; hindari ternary bersarang, gunakan if-else untuk logika kompleks.
  • Gunakan kurung untuk kejelasan — jangan andalkan precedence untuk ekspresi yang punya lebih dari dua operator; kurung membuat niat lebih jelas bagi pembaca.

← Sebelumnya: Tipe Data   Berikutnya: Seleksi Kondisi →

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