« poprzedni punkt  następny punkt »

2. Operatory i wyrażenia porównania

W wielu instrukcjach sterujących będą występować warunki podane jako wyrażenie relacyjne, konstruowane z zastosowaniem operatorów relacyjnych oraz operatorów równości - nierówności.
Np. w nawiasach instrukcji if występuje warunek, który może być takim wyrażeniem:

if (a > b) ...

Po części już omawialiśmy te operatory. Warto jednak usystematyzować podane wcześniej informacje oraz uzupełnić je o nowe fakty.

Operatory relacyjne (<, <=, >, >=):

  • są operatorami dwuargumentowymi
  • ich argumentami mogą być wyłącznie wyrażenia typów numerycznych (byte, char, short, int, long, float, double)
  • wyrażenia relacyjne (konstruowane z pomocą tych operatorów) dają zawsze w wyniku wartości typu boolean (true lub false)
Operatory relacyjne
Wyrażenie
(a i b - dowolne wyrażenia,
ale koniecznie
typu numerycznego)
Wynik
a > b
true jeśli wartość a jest większa od b
false w przeciwnym razie
a >= b
true jeśli wartość a jest większa lub równa b
false w przeciwnym razie
a < b
true jeśli wartość a jest mniejsza od b
false w przeciwnym razie
a <= b
true jeśli wartość a jest mniejsza lub równa b
false w przeciwnym razie

Przykłady:

int a =1, b = 3;
if (a > b) System.out.println("Większe");
else System.out.println("Mniejsze");
Wyprowadzi napis:
Mniejsze
int a =1, b = 3;
if (a + 1 > b/3) System.out.println("Większe");
else System.out.println("Mniejsze");

uwaga: priorytet operatorów relacyjnych jest niższy od priorytetu operatorów arytmetycznych - dzięki temu nie musimy pisać:
if ( (a + 1) > (b/3) ) ...
Wyprowadzi napis:
Większe
int a = 1, b = 3;
boolean wynik1 = a <= b;
boolean wynik2 = 1 >= a;
boolean wynik3 = a < 1;

uwaga: priorytet operatorów relacyjnych jest wyższy od priorytetu operatora przypisania - dzięki temu nie musimy pisać:
boolean wynik = (a <= b);
Zmienne będą miały następujące wartości:
wynik1 == true
wynik2 == true
wynik3 == false

Problem



Częstym błędem jest stosowanie zamiast operatora równości == znaku =. Należy się go wystrzegać, choć zwykle kompilator wykryje ten błąd i zwróci nam na niego uwagę.

Od grupy operatorów relacyjnych odróżnia się dwuargumentowe operatory równości (==) i nierówności (!=) - z dwóch powodów:

  • mają one niższy priorytet niż operatory relacyjne
  • ich argumentami mogą być wartości wszystkich typów (numerycznych, logicznych, referencyjnych), pod warunkiem jednak, że:
    • jeżeli jeden z argumentów jest typu numerycznego - to drugi też musi być typu numerycznego,
    • jeżeli jeden z argumentów jest typu boolean - to i drugi musi być tego typu,
    • jeżeli jeden z argumentów jest typu referencyjnego, to drugi musi być typu referencyjnego dającego się przekształcić do typu pierwszego albo literałem null (o przekształceniach typów będziemy mówić później; na razie możemy przyjąć, że obowiązuje nas ograniczenie porównywania na równość - nierówność tych samych typow referencyjnych np. referencji typu String lub referencji typu Para)

Konstruowane za pomocą operatorów równości-nierówności wyrażenia mają zawsze wynik typu boolean.

Operatory równości - nierówności
Wyrażenia
a == b
a != b
a i b dowolnego typu numerycznego
true jeśli wartość a jest równa wartości b
false w przeciwnym razie
true jeśl i wartość a nie jest równa wartości b
false w przeciwnym razie
a i b typu boolean
true jeśli a i b oba mają wartość true lub wartość false
false w przeciwnym razie
true jeśli a jest true, b false lub odwrotnie
false, jeśli a i b są oba true lub oba false
a i b typu referencyjnego
true jeśli obie referencje wskazują ten sam obiekt lub jeśli obie mają wartość null
false jeśli referencje odnoszą się do różnych obiektów, lub jedna z nich ma wartość null, a druga - nie
true jeśli obie referencje odnoszą się do innych obiektów lub jeśli jedna z nich jest null, a druga nie
false jeśli obie referencje wskazują ten sam obiekt lub jeśli obie mają wartość null

Przykład:

int a = 2, b = a + 1;
if ( a == b) System.out.println("tak");
else System.out.println("nie");

if ( a != --b) System.out.println("tak");   // 2
else System.out.println("nie");

int c = 4;
if ( a < b + 1 == b  < c) System.out.println("tak");  //3
else System.out.println("tak");

nie
nie
tak

Ten fragment kodu wypisze na konsoli dane podane obok.

Uwagi:

  1. przedrostkowa forma operatora -- zapewnia, że wartość b w przykładzie 2 będzie zmieniona przed użyciem w wyrażeniu porównania na nierówność;
  2. w przykładzie 3 korzystamy z tego, że priorytet operatora == jest niższy od priorytetu operatorów relacyjnych, a te z kolei są niższe od priorytetów operatorów arytmetycznych (+); na równość porównywane są zatem dwie wartości typu boolean, a ponieważ obie są true, to wynik porównania też jest true

Problem



Jednak - w zasadzie nie należy zapisywać w taki sposób jak w przykładzie oznaczonym // 3 złożonych wyrażeń. Raczej warto użyć nawiasów, które zwiększą czytelność kodu.
Zresztą porównywanie wartości boolowskich jest mało użyteczne, bo - w większości przypadków - zamiast niego należy stosować logiczne operatory koniunkcji czy alternatywy.

Przykład porównywania referencji:

// Klasa Para - to znana nam już klasa par liczb całkowitych
Para p1 = new Para(1,1);
Para p2 = new Para(1,1);
Para p3 = p1;

p1 == p2 // false, mimo że składniki par są takie same
p1 == p3 // true, bo wskazują na ten sam obiekt

String s1 = "1,1";
p1 == s1 // bląd w kompilacji, bo p1 jest typu Para, a s1 typu String

String s2 = "1," + 1;
s1 == s2 // false, bo są to referencje do różnych obiektów
s1 == null // false, bo s1 wskazuje na jakiś obiekt
s1.equals(s2) // true, bo zawartość łańcuchów znakowych jest taka sama.

Przypomnienie: porównywanie zawartości obiektów (na równość - nierówność) odbywa się zawsze za pomocą metody equals, zdefiniowanej w klasie obiektów.

Uwaga: W przeciwieństwie do klasy String, w klasie Para nie zdefiniowano metody equals. Jej użycie nie da zatem spodziewanych efektów. Tego w jaki sposób definiować metodę equals we własnych klasach - dowiemy się w przyszłym semestrze, wymaga to bowiem nieco bardziej zaawansowanej wiedzy "obiektowej", m.in. rozumienia pojęcia polimorfizmu.

Na koniec omawiania porównań warto zwrócić uwagę na liczby rzeczywiste. Otóż wszystkie porównania są zawsze dokładne. Ale reprezentacja liczb rzeczywistych w komputerze nie jest dokładna. Dlatego przy porównaniu liczb rzeczywistych powinniśmy sprawdzać nie tyle czy są one dokładnie równe, ale czy ich wartości są dostatecznie sobie bliskie. Więcej o tym w wykładzie o liczbach.


« poprzedni punkt  następny punkt »