Warunki i pętle
Cześć!
Zaczynamy kolejną część Wyzwania Java z Kodillą! Na początku omówimy sobie rozwiązanie ostatniej pracy domowej. Jak pamiętasz, zadaniem było utworzenie metody w klasie Flight
, która zwracałaby informacje o locie ("Flight from … to …"). Można było to zrobić na kilka sposobów, my zdecydowaliśmy się na zwracanie Stringa z informacjami o locie:

W dzisiejszym odcinku natomiast nauczymy się, czym są instrukcje warunkowe oraz pętle.

Instrukcje warunkowe: wprowadzenie
Zaczniemy od definicji, czyli odpowiedzi na pytanie: czym są instrukcje warunkowe? Mówiąc w skrócie, instrukcje warunkowe umożliwiają naszej aplikacji wykonywanie różnych akcji w zależności od tego, czy zdefiniowany przez nas warunek został spełniony bądź nie. Innymi słowy, instrukcja warunkowa działa na zasadzie: "jeśli warunek jest spełniony, zrób to, a w przeciwnym wypadku, zrób tamto".
Już za chwilę nie będziesz sobie wyobrażać świata bez instrukcji warunkowych Jest to podstawa programowania, ponieważ pozwala na to, aby Twój program rozpoznawał okoliczności, w którym ma (lub nie ma) wykonać wskazane operacje.
Metafora: instrukcje warunkowe
Instrukcje warunkowe możemy wyobrazić sobie jako sortownię poczty, która w zależności np. od rozmiaru czy wagi przesyłek kieruje je do różnych ciężarówek. W tej sortowni jest jednak zasada, że można zadawać tylko pytania, na które odpowiedź to "tak" lub "nie". Na przykład:
- Czy paczka ma naklejkę "ostrożnie – szkło"? Jeśli tak, musi jechać ciężarówką A.
- Czy paczka jest standardowych rozmiarów? Jeśli tak, musi jechać ciężarówką B.
- Czy paczka waży ponad 10 kg? Jeśli tak, musi jechać ciężarówką C.
- W przeciwnym wypadku musi jechać ciężarówką D.
Instrukcja if
Zaczniemy od podstawowej instrukcji warunkowej, czyli if
(z angielskiego: jeżeli). Jej składnia wygląda następująco:

Zaraz, zaraz. Co to jest boolean? Boolean to typ danych, który może mieć dwie wartości: true lub false. Czyli, w uproszczeniu, "tak" lub "nie". Zmienną typu boolean możemy utworzyć w następujący sposób:

Zwróć uwagę na sposób przypisywania wartości: nie dodajemy cudzysłowu, po prostu piszemy true lub false.
Wróćmy do instrukcji warunkowej. Kod w klamrach wykona się, gdy wyrażenie zwróci wartość true. Samo wyrażenie może być zdefiniowane na wiele sposobów, oto kilka nich:

Co ważne, jeśli w wyrażeniu umieścimy kod, który nie będzie zwracał booleana, całość się nie skompiluje. Umieszczenie np. liczby 51 w wyrażeniu spowoduje błąd. 51 jest intem (liczbą), a nie booleanem.
Przećwiczmy wyrażenia warunkowe na przykładzie z telefonem komórkowym. Spróbuj dodać metodę, która wyświetli tekst “This phone is too expensive”, jeśli cena urządzenia jest wyższa niż 550:
- w klasie
MobilePhone
dodaj nową metodę typu public void o nazwiecheckPrice
, - wewnątrz tej metody zapisz w Javie następującą instrukcję warunkową: "jeżeli cena urządzenia (czyli
this.price
) jest wyższa niż 500, wyświetl (za pomocą konstrukcjiSystem.out.println
) tekst "This phone is too expensive", - na końcu, w klasie
Main
, wywołaj tę metodę dla każdego z telefonów, które mamy (phone
,oldPhone
,mediumPhone
).
Gotowe? Rozwiązanie tego ćwiczenia znajdziesz poniżej:

No dobrze, mamy już tekst, który pokazuje się tylko pod pewnym warunkiem. Gdy uruchomisz nasz program, zauważysz, że mimo ze wywołaliśmy metodę trzy razy, tekst pojawił się tylko raz (ponieważ warunek został spełniony tylko w jednym przypadku). Czas zatem wzbogacić nasz skrypt i sprawić, by program wyświetlał różne teksty w zależności od sytuacji.
Instrukcja if/else if/else
Do znanej nam już konstrukcji z if
możemy dołożyć kolejny element, który sprawi, że zostanie wykonana jakaś operacja, gdy zdefiniowany przez nas warunek nie zostanie spełniony (czyli okaże się fałszywy). Takim elementem jest else
, który z grubsza możemy przetłumaczyć jako "w przeciwnym wypadku". Cała struktura wygląda następująco:

Jeśli wyrażenie jest true, wykona się fragment kodu po if
, w przeciwnym wypadku wykona się fragment kodu po else
.
To jednak nie wszystko. Możemy chcieć przecież sprawdzać w naszym kodzie więcej niż jeden warunek. W naszym przykładzie mogą to być różne zakresy cenowe. Co wtedy? W takiej sytuacji przychodzi nam z pomocą wyrażenie else if
:

Mamy fragmenty kodu oznaczone cyframi od 1 do 4, jednak tylko jeden z nich się wykona. Jeśli wyrażenie2
będzie prawdziwe (true), kolejne wyrażenie już się nie wykona. To bardzo ważne. Fragment else
jest opcjonalny - może, ale nie musi istnieć. Jeśli żadne z wyrażeń nie zwróci true, wykona się else
.
Łączenie warunków - operatory
Świetnie! Umiemy już zdefiniować kilka warunków i wykonać jakiś kod w zależności od tego, czy któryś z nich zostanie spełniony. A co, jeżeli chcielibyśmy wykonać jakąś akcję w momencie, gdy zostają spełnione dwa warunki, na przykład cena telefonu wyższa niż 500 i rok produkcji późniejszy niż 2017? Tu pomocą przychodzi operator &&
(koniunkcja, czytamy jako "i"):

Możemy też chcieć, by kod wykonał się, gdy spełniony jest jeden lub drugi zdefiniowany warunek. W takim przypadku zastosujemy alternatywę - ||
(dwie pionowe kreski (pipe) w pobliżu klawisza Enter na klawiaturze:

Powyższy kod wykona się, gdy cena jest większa od 500 lub rok jest większy niż 2017.
Warunków logicznym w wyrażeniu warunkowym możemy umieszczać wiele, jednak nie należy przesadzać z ich liczbą. Nie ma ściśle określonych zasad, ale warto zachować rozsądek, aby kod był czytelny.
Ćwiczenie: instrukcje warunkowe
Spróbujmy to przećwiczyć. Do metody checkPrice()
dodaj warunki sprawdzające (walidujące) cenę:
- jeśli cena jest niższa od 200, wyświetl "price is very good",
- jeśli cena jest z przedziału 200-299, wyświetl "price is good",
- jeśli cena jest z przedziału 300-549, wyświetl "price could be lower",
- jeśli cena jest równa lub wyższa od 550, wyświetl "this phone is too expensive".
Poświęć chwilę na to ćwiczenie, a dopiero później zerknij na rozwiązanie:

Zauważ, że nie zostały użyte zakresy liczb (np. price > 200 && price < 300
). Odpowiednie dopasowanie warunków rozwiązało problem, a kod jest czytelniejszy.
Pętle
Warunki i operatory są już jasne - czas przejść krok dalej!
Zdarza się, że chcemy wykonać ten sam kod kilka razy z rzędu, albo dopóki jakiś warunek jest prawdziwy. W takich przypadkach przyjdą nam z pomocą pętle. Pętla to nic innego, jak wykonywanie tych samych czynności w kółko. Każde wykonanie tych operacji, czyli każdy "obrót" pętli, nazywamy iteracją.
Metafora: pętla
Załóżmy, że Twój kot często chowa się w pudełku, kiedy chcesz go zabrać do weterynarza. Potrzebujesz w takim razie znaleźć kota, a masz przed sobą wiele pudełek.
- jeśli pudełko jest za małe dla kota, przejść do kolejnego pudełka,
- jeśli pudełko jest oklejone taśmą klejącą i kot nie mógł do niego wejść, przejść do kolejnego pudełka,
- otworzyć pudełko, i jeśli nie ma w nim kota, przejść do kolejnego pudełka,
- jeśli jest w nim kot, wyjąć kota i nie sprawdzać kolejnych pudełek.
W Javie składnia pętli wygląda następująco:

Brzmi tajemniczo? Już wyjaśniamy.
Wyrażenie początkowe zostanie wykonane tylko raz, przed uruchomieniem pętli. Zazwyczaj w tym miejscu tworzymy nową zmienną (licznik), za pomocą której będziemy sterować liczbą wykonań (obrotów/iteracji) pętli.
Wyrażenie warunkowe określa warunek, jak długo dana pętla ma się wykonywać. Ten fragment uruchamiany jest przed każdym kolejnym przebiegiem pętli.
Wyrażenie końcowe to zazwyczaj zmiana wartości licznika (przykładowo, po każdym wykonaniu kodu zwiększamy wartość licznika o jeden). Wyrażenie końcowe wykonywane jest po każdym pojedynczym obiegu pętli.
Wytłumaczmy to sobie na konkretnym przykładzie:

W powyższej pętli:
- Jako wyrażenie początkowe tworzymy zmienną typu int z wartością 0. Jej zwyczajowa nazwa to
i
, czyli "iterator", ale tak naprawdę mogłaby być dowolna. - Następnie sprawdzany jest warunek, czy
i
jest mniejsze od 10. - Jeśli tak, wykonywany jest kod wewnątrz pętli – wyświetlanie aktualnej wartości
i
. Jeśli warunek nie jest spełniony, pętla jest przerywana, a kolejna iteracja się nie wykona. - Następny krok to wykonanie wyrażenia końcowego:
i++
oznacza zwiększenie wartościi
o jeden (można to również zapisać tak:i = i + 1
). Dalej następuje kolejny obrót pętli: sprawdzenie, czy wyrażenie warunkowe jest spełnione, a następnie wykonanie kodu lub przerwanie pętli.
Wszystko jasne? Zatem czas na kilka ćwiczeń!
Ćwiczenie: pętle
Napisz metodę, która wyświetli wszystkie liczby parzyste od zera do 20 włącznie (metoda na czas testów może istnieć w klasie MobilePhone
).
Ta pętla będzie bardzo podobna do przykładu omówionego wyżej, z tym, że zamiast zwiększania iteratora o jeden (i++
), zwiększymy go o dwa (i+2
).
Gotowe? Swoje rozwiązanie możesz sprawdzić poniżej:

Założenie nie jest skomplikowane: zaczynamy od zera i licznik zwiększamy o dwa przy każdym przebiegu pętli. Na sztywno określiliśmy maksymalną liczbę (21). Spróbuj teraz to zmienić: przekaż w argumencie metody maksymalną wartość, jaka może zostać wyświetlona.
Rozwiązanie:

Pamiętaj o przekazaniu argumentu w momencie wywoływania metody!
Możemy iść krok dalej. Wartości zawsze wyświetlane są od zera – zmieńmy to. Spróbuj przekazać wartość minimalną, oraz wykonaj kilka testów: wyświetl liczby parzyste od 3 do 8, od -1 do 11 i od 17 do 33.
Czy Twoje wyniki są poprawne? Domyślamy się, że w pierwszym momencie nie, i zastanawiasz się czemu tak się stało. Wykonując przykładowy test phone.displayEvenNumbers(3, 8);
, otrzymujemy cyfry 3, 5 oraz 7. Masz pomysł, jak naprawić wyświetlanie liczb? Podpowiedź: przed każdym wyświetlaniem należy zweryfikować, czy liczba jest podzielna bez reszty przez dwa. W Javie istnieje specjalny operator – %
(zwany fachowo modulo), który nam w tym pomaga. Używa się go następująco:
5%2
zwraca 1 – reszta z dzielenia 5 przez 2 to jeden (w piątce zawierają się dwie dwójki czyli cztery, zostaje reszta 1),12%3
zwraca 0 – w dwunastce znajdują się cztery trójki, cztery razy trzy daje 12, więc reszta jest równa 0.
Będziesz potrzebować jeszcze jednego operatora: porównania. W Javie jego symbol to ==
. Pojedynczy znak =
to przypisanie wartości z prawej strony do lewej. Podwójny symbol ==
sprawdza, czy prawa strona jest równa lewej, i zwraca wartość typu boolean.
Skorzystaj z poznanej instrukcji warunkowej if
, aby naprawić metodę wyświetlającą wyłącznie parzyste liczby.
Udało się? Pewnie nie było łatwo ;) Porównaj poniższy kod ze swoim rozwiązaniem:

Wyrażenie końcowe zmienione zostało na i++
zamiast i=i+2
– w ten sposób sprawdzamy każdy element, a nie co drugi. Dalej, dodany został if
– obliczamy resztę z dzielenia i sprawdzamy za pomocą znaków ==
, czy jest ona równa 0.
Pamiętasz, od czego zaczęliśmy pisanie metod displayEvenNumbers
? Było to zwykłe wyświetlanie liczb w stałym zakresie bez żadnej weryfikacji. Po kilku zmianach mamy dość rozbudowany kod. W taki właśnie sposób staraj się tworzyć własne rozwiązania. Na początku coś prostego, później dodanie jednego warunku, drugiego, trzeciego, aż osiągniesz ostateczny efekt. Na tym polega pisanie oprogramowania: nie tworzymy ostatecznego rozwiązania za pierwszym razem. Krok po kroku dążymy do celu.
Porównywanie obiektów
Wróćmy teraz do naszej wyszukiwarki lotów. W pierwotnych założeniach zostało powiedziane, że pierwszy etap to wyszukiwanie lotów z danego miasta. Mamy klasę Flight
, ale nie mamy bazy lotów – pojawią się już jutro. Problem, który teraz opiszemy, dotyczy innego zagadnienia.
Mamy taki kod:

Staramy się w nim porównać dwa Stringi. Na pierwszy rzut oka wydaje się, że powinno się wyświetlić "The same values", jednak tak się nie dzieje. Dlaczego? Porównanie ==
stosujemy wyłącznie do typów prymitywnych, takich jak int. String to obiekt. Jak odróżnić typ prymitywny od obiektu? Należy zapamiętać, że nazwa typu obiektowego zawsze zaczyna się wielką literą. Inaczej jest w przypadku typu prymitywnego – używamy małych liter.
Jak w takim razie porównać dwa obiekty? Za pomocą metody equals
. Każda klasa w Javie, nawet gdy jawnie nie napiszemy wspomnianej metody, zawiera ją.
Zmodyfikowany kod:

Jak widzisz, wystarczyło zmienić wyrażenie first == second
na first.equals(second)
.
Zapamiętaj: zawsze, gdy chcesz porównać obiekty, używaj equals. Więcej na ten temat dowiesz się w trakcie bootcampu.
Zadanie domowe
Ponownie myślami wracamy do wyszukiwarki lotów :) Mamy bazę i nazwę jednego miasta, z którego chcemy znaleźć loty. Jak to zrobić? Pętlą for
i instrukcją if
. Wystarczy, że przejdziemy po wszystkich lotach z bazy i sprawdzimy, czy lotnisko startowe równe jest miastu, jakie zakładamy. Jeśli tak, wyświetlimy lot. Implementację zrobimy jutro, gdy będziemy mieć bazę lotów.
Na razie wykonamy kilka ćwiczeń, które przygotują nas do tego zadania:
- Napisz metodę, która wyświetli liczbę liczb podzielnych przez 3 lub przez 5 z dowolnego zakresu. Przykład: wywołujemy metodę
isDivived(4, 18)
. Jaka liczba powinna zostać zwrócona? Przeanalizujmy liczby podzielne przez 3 lub przez 5 z zakresu 4-18: 5, 6, 9, 10, 12, 15, 18. Daje to 7 liczb i taki wynik powinien zostać wyświetlony. - Napisz metodę, która zwróci sumę liczb parzystych z podanego zakresu. Przykład: metoda to
getEvenSum(5, 9)
. Parzyste liczby w podanym zakresie to 6 i 8. Ich suma to 14. - Wyświetl wszystkie liczby w kolejności malejącej z podanego zakresu. Przykład: metoda
getNumber(5, -2)
powinna wyświetlić: 5, 4, 3, 2, 1, 0, -1, -2.
Miłej nauki i do następnego razu!