KezdőOlvasási idő: ~17 perc

Operátorok

Aritmetikai, logikai, bitenkénti, összehasonlító operátorok és rövidzár kiértékelés

A Java operátorai és kifejezései: aritmetikai, relációs, logikai, bitenkénti, értékadó, ternary, instanceof operátorok, precedencia és kiértékelési sorrend.


1. Definíció

Mi ez?

Az operator egy szimbólum, amely egy vagy több operanduson hajt végre műveletet és eredményt (értéket) produkál. A kifejezés (expression) operátorok és operandusok kombinációja, amely egyetlen értékre értékelődik ki.

Miért fontos?

  • Alapvető építőkő: minden algoritmus, feltétel és ciklus operátorokra épül
  • Teljesítmény: a megfelelő operator választás (pl. bitwise shift vs. szorzás) jelentős különbséget hozhat
  • Olvashatóság: a precedencia és short-circuit kiértékelés megértése elengedhetetlen a hibamentes kódhoz

Hol helyezkedik el?

Az operátorok a Java nyelv szintaktikai alaprétegéhez tartoznak — közvetlenül a típusok és változók fölött, és a vezérlési szerkezetek (control structures) alatt.


2. Alapfogalmak

2.1 Aritmetikai operátorok (Arithmetic operators)

Operator Leírás Példa Eredmény
+ Összeadás 5 + 3 8
- Kivonás 5 - 3 2
* Szorzás 5 * 3 15
/ Osztás 7 / 2 3 (integer division!)
% Maradékos osztás (modulo) 7 % 2 1
++ Inkrementálás i++ / ++i pre/post increment
-- Dekrementálás i-- / --i pre/post decrement

⚠️ Integer division: ha mindkét operandus egész szám, az eredmény is egész (a tizedes rész levágódik).

2.2 Relációs operátorok (Relational / Comparison operators)

Operator Leírás Példa
== Egyenlő a == b
!= Nem egyenlő a != b
> Nagyobb a > b
< Kisebb a < b
>= Nagyobb vagy egyenlő a >= b
<= Kisebb vagy egyenlő a <= b

🔑 Primitíveknél == az értéket hasonlítja össze, objektumoknál a referenciát.

2.3 Logikai operátorok (Logical operators)

Operator Leírás Short-circuit?
&& Logikai ÉS ✅ Igen — ha a bal false, a jobb nem értékelődik ki
|| Logikai VAGY ✅ Igen — ha a bal true, a jobb nem értékelődik ki
! Logikai NEM
& Logikai ÉS (nem short-circuit) ❌ Nem
| Logikai VAGY (nem short-circuit) ❌ Nem

2.4 Bitenkénti operátorok (Bitwise operators)

Operator Leírás Példa
& Bitenkénti ÉS 0b1010 & 0b11000b1000
| Bitenkénti VAGY 0b1010 | 0b11000b1110
^ Bitenkénti XOR 0b1010 ^ 0b11000b0110
~ Bitenkénti NEM (komplemens) ~0b10100b...0101
<< Bal shift (left shift) 1 << 38
>> Jobb shift, előjeles (signed right shift) -8 >> 2-2
>>> Jobb shift, előjel nélküli (unsigned right shift) -1 >>> 2815

2.5 Értékadó operátorok (Assignment operators)

Operator Ekvivalens
= Egyszerű értékadás
+= a = a + b
-= a = a - b
*= a = a * b
/= a = a / b
%= a = a % b
&= a = a & b
|= a = a | b
^= a = a ^ b
<<= a = a << b
>>= a = a >> b
>>>= a = a >>> b

🔑 A compound assignment operátorok (pl. +=) implicit kasztolást tartalmaznak: short s = 1; s += 1; ✅ fordítás OK, de s = s + 1; ❌ compile error (int → short).

2.6 Ternary operator (feltételes kifejezés)

eredmeny = (feltétel) ? értékHaTrue : értékHaFalse;

Ez az egyetlen háromoperandusú (ternary) operator a Java-ban.

2.7 instanceof operator

if (obj instanceof String s) {   // pattern matching (Java 16+)
    System.out.println(s.length());
}
  • null instanceof Bármi → mindig false
  • Java 16+ óta pattern matching: deklarálhat változót az instanceof-on belül

2.8 Precedencia tábla (Operator Precedence)

Prioritás Operátor(ok) Asszociativitás
1 (legmagasabb) () [] . Balról jobbra
2 ++ -- (postfix) Balról jobbra
3 ++ -- (prefix) + - (unáris) ~ ! Jobbról balra
4 (type) cast Jobbról balra
5 * / % Balról jobbra
6 + - Balról jobbra
7 << >> >>> Balról jobbra
8 < <= > >= instanceof Balról jobbra
9 == != Balról jobbra
10 & Balról jobbra
11 ^ Balról jobbra
12 | Balról jobbra
13 && Balról jobbra
14 || Balról jobbra
15 ? : Jobbról balra
16 (legalacsonyabb) = += -= stb. Jobbról balra

2.9 Kiértékelési sorrend vs. precedencia

Ezt a két fogalmat nagyon gyakran összekeverik.

  • A precedencia azt mondja meg, hogyan csoportosul a kifejezés
  • A kiértékelési sorrend azt mondja meg, mikor fut le az egyes operandusok kiértékelése

A Java garantálja az operandusok balról jobbra történő kiértékelését, de ettől még ugyanúgy szükség van zárójelezésre az olvashatóság érdekében.


3. Gyakorlati használat

Mikor melyik operátort használjuk?

Feladat Ajánlott Miért?
Páros/páratlan vizsgálat n % 2 == 0 Egyszerű, olvasható
2 hatványával szorzás/osztás n << 1 / n >> 1 Teljesítmény (bár a JIT amúgy is optimalizál)
Null-safe ellenőrzés obj != null && obj.method() Short-circuit megvéd a NullPointerException-tól
Egyszerű if-else érték Ternary ? : Tömörebb, de ne ágyazd egymásba!
Flag-ek kezelése Bitwise &, |, ^ Memória-hatékony, gyors
Típusellenőrzés instanceof + pattern matching Java 16+ modern idiom

Gyakorlati hüvelykujjszabályok

  • A feltételekben az olvashatóság fontosabb, mint a rövidség
  • Zárójelezz akkor is, ha a precedencia alapján “működne” nélküle
  • Kerüld a mellékhatásos kifejezéseket az összetett logikai feltételekben
  • Bitenkénti operátort csak ott használj, ahol maga a domain is bit-alapú
  • Ha számít a null-biztonság, sokszor az Objects.equals(a, b) a jobb választás

4. Kód példák

4.1 Alapszintű példák

// Integer division — gyakori meglepetés
int a = 7 / 2;        // 3, NEM 3.5!
double b = 7.0 / 2;   // 3.5 — legalább az egyik operandus double

// Pre vs. post increment
int i = 5;
int x = i++;  // x = 5, i = 6 (post: először értékadás, aztán növelés)
int y = ++i;  // y = 7, i = 7 (pre: először növelés, aztán értékadás)

// Modulo negatív számoknál
int mod = -7 % 3;  // -1 (az eredmény előjele az osztandó előjelét követi)

4.2 Short-circuit evaluation

// ✅ Helyes: short-circuit megvéd a NullPointerException-tól
String name = null;
if (name != null && name.length() > 0) {
    System.out.println(name);
}

// ❌ Hibás: & nem short-circuit, NullPointerException lesz!
if (name != null & name.length() > 0) {  // 💥 NPE!
    System.out.println(name);
}

4.3 Bitwise flag kezelés

// Flag-ek definiálása
static final int READ    = 0b0001;  // 1
static final int WRITE   = 0b0010;  // 2
static final int EXECUTE = 0b0100;  // 4

// Flag beállítása
int permissions = READ | WRITE;          // 0b0011 = 3

// Flag ellenőrzése
boolean canRead = (permissions & READ) != 0;   // true

// Flag törlése
permissions = permissions & ~WRITE;             // 0b0001 = 1

// Flag váltása (toggle)
permissions = permissions ^ EXECUTE;            // 0b0101 = 5

4.4 Ternary — jó és rossz használat

// ✅ Egyszerű, olvasható
String status = (age >= 18) ? "felnőtt" : "kiskorú";

// ❌ Egymásba ágyazott ternary — olvashatatlan!
String category = (age < 13) ? "gyerek" : (age < 18) ? "tinédzser" : "felnőtt";

// ✅ Helyette if-else vagy switch
String category;
if (age < 13) category = "gyerek";
else if (age < 18) category = "tinédzser";
else category = "felnőtt";

4.5 Compound assignment implicit cast

short s = 10;
// s = s + 1;   // ❌ Compile error! (s + 1 = int, int → short szűkítő)
s += 1;         // ✅ OK! A += implicit kasztolást tartalmaz: s = (short)(s + 1)

byte b = 127;
b += 1;         // ✅ Fordul, DE overflow! b = -128

5. Trade-offok

Szempont Opció A Opció B Mikor melyik?
Olvashatóság vs. tömörség if-else Ternary ? : Ternary egyszeri egyszerű választáshoz; if-else komplexebb logikához
Bitwise vs. hagyományos n * 2 n << 1 Modern JVM-en nincs érdemi sebességkülönbség; n * 2 olvashatóbb
Short-circuit vs. eager && / || & / | Szinte mindig short-circuit; eager csak ha mindkét oldali mellékhatás kell
== vs. equals() == .equals() == primitívekhez és referencia-összehasonlításhoz; .equals() tartalom-összehasonlításhoz

6. Gyakori hibák

❌ 6.1 Integer division meglepetés

double result = 1 / 3;          // 0.0, nem 0.333!
double correct = 1.0 / 3;       // 0.333...  ✅
double alsoCorrect = (double) 1 / 3;  // 0.333...  ✅

❌ 6.2 Precedencia hiba

// Szándék: (a & b) != 0
if (a & b != 0) { }   // ❌ Valójában: a & (b != 0) — a != megelőzi a &-t!
if ((a & b) != 0) { }  // ✅ Zárójelezés!

❌ 6.3 == vs. equals objektumoknál

String s1 = new String("hello");
String s2 = new String("hello");
s1 == s2;        // false — különböző referenciák!
s1.equals(s2);   // true  ✅

❌ 6.4 Pre/post increment mellékhatás

int i = 0;
int result = i++ + ++i;  // ❌ Zavaros, platform-specifikusnak tűnhet
// Java-ban jól definiált (result = 0 + 2 = 2), de KERÜLENDŐ!

❌ 6.5 Overflow compound assignment-nél

byte b = 127;
b += 1;     // -128! Nincs compile-time warning, silent overflow

7. Mélymerülés

7.1 Operator overloading hiánya

A Java — a C++-szal és a Kotlin-nal ellentétben — nem támogatja az operator overloading-ot. Egyetlen kivétel a + operator, amely a String concatenation-höz belső kezelést kap (a compiler StringBuilder-re vagy invokedynamic-ra írja át Java 9+).

7.2 Kifejezések kiértékelési sorrendje (JLS §15.7)

A Java Language Specification garantálja, hogy az operandusok balról jobbra értékelődnek ki (left-to-right evaluation). Ez nem azonos a precedenciával:

int i = 0;
int r = (i = 1) + (i = 2);  // r = 1 + 2 = 3, NEM 2 + 2!
// A bal operandus előbb értékelődik ki, mint a jobb

7.3 Numeric promotion szabályok

  • Ha bármelyik operandus double → az eredmény double
  • Különben, ha bármelyik float → eredmény float
  • Különben, ha bármelyik long → eredmény long
  • Különben: mindkettő int-re promótálódik (akkor is, ha byte + byte!)
byte a = 10, b = 20;
// byte c = a + b;  // ❌ Compile error! a + b = int
int c = a + b;      // ✅

7.4 NaN és Infinity viselkedés

double nan = Double.NaN;
nan == nan;           // false! NaN soha nem egyenlő önmagával
Double.isNaN(nan);    // true  ✅

double inf = 1.0 / 0; // Infinity, nem ArithmeticException!
int zero = 1 / 0;     // 💥 ArithmeticException (integer division)

7.5 Switch expression vs. ternary (Java 14+)

// Java 14+ switch expression — olvashatóbb, mint az egymásba ágyazott ternary
String type = switch (code) {
    case 1 -> "admin";
    case 2 -> "user";
    default -> "guest";
};

7.6 A bitmaszkok ma is valós use case-eket fednek le

Hiába használunk sokszor enumokat és boolean mezőket, a bitmaszkok ma is megjelennek jogosultságkezelésben, bináris protokollokban, fájlformátumokban és alacsony szintű framework kódban.

A jó szabály nem az, hogy “soha ne használj bitenkénti operátorokat”, hanem az, hogy csak akkor használd őket, ha maga az adatmodell valóban bit-alapú, és ezt egyértelműen dokumentálod.

7.7 A mellékhatásos kifejezések rontják a karbantarthatóságot

Ez teljesen legális Java:

if (cache != null && cache.refreshIfExpired()) {
    // ...
}

De az állapotmódosítás és a feltételvizsgálat összekeverése nehezebbé teszi a hibakeresést. Üzleti kódban sokszor jobb külön lépésekre bontani a logikát.


8. Interjúkérdések

Mi a különbség a precedencia és a kiértékelési sorrend között?

A precedencia azt határozza meg, hogyan csoportosítja a fordító a kifejezést, a kiértékelési sorrend pedig azt, hogy milyen sorrendben futnak le az operandusok. Java-ban az operandusok balról jobbra értékelődnek ki, de a precedencia továbbra is meghatározza a szerkezetet.

Miért jobb feltételekben általában a `&&`, mint az `&`?

Mert a && short-circuit módon működik. Ha a bal oldal már false, akkor a jobb oldal nem fut le, ami egyszerre olvashatóbb és biztonságosabb, például null guardok esetén.

Miért lesz a `byte + byte` eredménye `int`?

Mert a Java számpromóciós szabályokat alkalmaz az aritmetikai műveletek előtt. Az int-nél kisebb egész típusokat előbb int-re emeli, ezért az eredmény is int lesz.

Mikor jó választás a ternary operátor?

Akkor, ha mindkét ág rövid, mellékhatásmentes és valóban egy értéket fejez ki. Ha egymásba ágyazás vagy összetett logika jelenik meg, általában jobb az if-else vagy a switch.


9. Szószedet

Fogalom Jelentés
Operator Szimbólum, amely operandus(ok)on műveletet hajt végre
Operand Az az érték, amelyen az operator dolgozik
Expression Operátorok és operandusok kombinációja, amely értékre értékelődik ki
Precedence Operátorok kiértékelési sorrendjének prioritása
Associativity Ha azonos a precedencia, a kiértékelés iránya (balról/jobbról)
Short-circuit evaluation Logikai kifejezés rövidített kiértékelése (a jobb oldal nem fut le, ha az eredmény már eldőlt)
Bitwise Bitekre vonatkozó művelet
Ternary operator Háromoperandusú feltételes kifejezés (? :)
Numeric promotion Kisebb típusok automatikus konverziója nagyobb típusra műveletnél
Compound assignment Összetett értékadás (+=, -=, stb.), implicit kasztolással
instanceof Típusellenőrző operator, Java 16+ óta pattern matching támogatással
Unary operator Egyoperandusú operátor (pl. !, ~, ++)
Binary operator Kétoperandusú operátor (pl. +, &&)

10. Gyorsreferencia

  • Integer division: 7 / 2 = 3 — használj 7.0 / 2-t vagy explicit cast-ot ha tizedest akarsz
  • Short-circuit: && és || → a jobb oldalt nem értékeli ki, ha az eredmény már eldőlt
  • Precedencia: ha kétely merül fel, zárójelezz(a & b) != 0
  • == vs. equals: == primitívekhez, .equals() objektumokhoz
  • Compound assignment: += implicit cast-ot tartalmaz → s += 1 fordul, s = s + 1 nem (ha s short/byte)
  • Numeric promotion: byte + byte = int → explicit cast kell a visszaadáshoz
  • NaN: Double.NaN != Double.NaN → használj Double.isNaN()-t
  • instanceof null: null instanceof Bármi → mindig false
  • Bitwise shift: 1 << n = 2ⁿ — hasznos flag-ekhez és hatványokhoz
  • Ternary: max 1 szint mélyen — ha bonyolultabb, használj if-else-t vagy switch expression-t
  • Evaluation order: JLS garantálja a balról-jobbra kiértékelést, de ne építs rá mellékhatásokkal!
  • Operator overloading: Java-ban nincs — kivéve a + String concatenation-höz

🎮 Játékok

10 kérdés