Spis treści
Zakres metod¶
W podrozdziale Instrukcji Warunkowych Bloki kodu i zakres zmiennych dowiedzieliśmy się, że zmienne tworzone w metodach muszą być zdefiniowane, zanim zostaną użyte – poniższy przykład nie skompiluje się, ponieważ kompilator w zaznaczonej linii nie wie jeszcze, czym jest "liczba":
public class UzyciePrzedDefinicja { public static void main(String[] args) { System.out.println("Liczba = " + liczba); int liczba = 5; } }
Wynik próby kompilacji:
Wróćmy do programu liczącego sumę dwóch liczb:
public class WypiszSume { public static void main(String[] args) { wypiszSume(100, 200); wypiszSume(-5, -20); wypiszSume(0, 0); } public static void wypiszSume(int a, int b) { System.out.println(a + b); } }
Pytanie: dlaczego w tym przypadku kompilacja kończy się sukcesem, a program, po uruchomieniu, wypisuje na ekran sumę podanych liczb, pomimo, że metodę wypiszSume używamy zanim zostanie ona zapisana w kodzie tego programu?
Otóż, metody w klasach nie muszą być w zdefiniowane w kolejności użycia – kompilator analizuje cały plik źródłowy i wie, jakie metody utworzyliśmy. Stąd kompilator nie protestuje widząc linię:
wypiszSume(100, 200);
ponieważ, po analizie całego pliku, wie, że wypiszSume jest metodą z dwoma argumentami, która nie zwraca żadnej wartości.
Wywoływanie metod¶
Co w zasadzie dzieje się, gdy wywołujemy metodę?
Pytanie: jaki będzie wynik działania poniższego programu (co po kolei się zadzieje i jakie komunikaty zobaczymy na ekranie):
import java.util.Scanner; public class KolejnoscInstrukcji { public static void main(String[] args) { System.out.println("Prosze podac liczbe:"); int podanaLiczba = getInt(); System.out.println("Teraz policzymy kwadrat tej liczby."); double liczbaDoKwadratu = policzKwadrat(podanaLiczba); System.out.println( "Kwadrat tej liczby wynosi " + liczbaDoKwadratu ); } public static int getInt() { return new Scanner(System.in).nextInt(); } public static double policzKwadrat(int liczba) { return liczba * liczba; } }
Wynikiem działania programu będzie:
Komunikaty zostały wyświetlone w tej kolejności, ponieważ gdy wywołujemy metodę, to skaczemy do niej z miejsca, gdzie do tej pory odbywało się wykonywanie programu. W komentarzach poniżej została zapisana kolejność wykonywania instrukcji w powyższym programie:
Strzałki obrazują przepływ działania programu:
- najpierw wypisujemy komunikat z prośbą o podanie liczby (1),
- następnie, gdy pobieramy od użytkownika liczbę (2), skaczemy z metody main do metody getInt (3),
- wracamy do metody main i wypisujemy tekst instrukcją (4),
- ponownie skaczemy z metody main (5) – tym razem do metody policzKwadrat (6),
- po powrocie, wypisujemy wynik obliczenia (7).
Zmienne lokalne¶
W poprzednich rozdziałach korzystaliśmy ze zmiennych w metodzie main, na przykład w programie z trzeciego rozdziału, który liczył pole i obwód koła użyliśmy czterech zmiennych (promienKola, pi, poleKola, oraz obwodKola):
public class ObwodPoleKola { public static void main(String[] args) { int promienKola = 8; double pi = 3.14; double poleKola = pi * promienKola * promienKola; double obwodKola = 2 * pi * promienKola; System.out.println("Pole kola wynosi: " + poleKola); System.out.println("Obwod kola wynosi: " + obwodKola); } }
Napiszmy metodę, która będzie liczyła dla nas pole koła na podstawie jego promienia:
public class PoleKola { public static void main(String[] args) { System.out.println( "Pole kola o promieniu 2 wynosi " + obliczPoleKola(2) ); } public static double obliczPoleKola(int promienKola) { double promienDoKwadratu = promienKola * promienKola; return 3.14 * promienDoKwadratu; } }
Metoda obliczPoleKola:
- przyjmuje jeden argument – promień koła, którego pole chcemy policzyć,
- wylicza kwadrat promienia koła i zapisuje go w zmiennej promienDoKwadratu,
- zwraca wyliczone pole koła przez przemnożenie promienia do kwadratu przez wartość liczby PI.
Pytanie: czy w metodzie main możemy odnieść się do zmiennej promienDoKwadratu, którą zdefiniowaliśmy w metodzie obliczPoleKola?
Spróbujmy:
public class PoleKolaZBledem { public static void main(String[] args) { System.out.println( "Pole kola o promieniu 2 wynosi " + obliczPoleKola(2) ); System.out.println( "Promien kola do kwadratu wynosi: " + promienDoKwadratu ); } public static double obliczPoleKola(int promienKola) { double promienDoKwadratu = promienKola * promienKola; return 3.14 * promienDoKwadratu; } }
Próba kompilacji powyższego programu zakończy się następującym błędem:
Kompilator poinformował nas, że nie wie, czym jest "promienDoKwadratu". Wynika to z faktu, że zmienna promienDoKwadratu jest lokalna dla metody obliczPoleKola i jest niedostępna nigdzie indziej w programie poza ciałem tej metody.
Jest to bardzo ważna informacja:
Zmienna promienDoKwadratu dostępna jest jedynie w metodzie obliczPoleKola, od momentu jej (zmiennej) zdefiniowania, do końca metody obliczPoleKola – po zakończeniu działania tej metody, zmienna promienDoKwadratu przestaje istnieć:
public class PoleKolaZBledem { public static void main(String[] args) { System.out.println( "Pole kola o promieniu 2 wynosi " + obliczPoleKola(2) ); // w metodzie main zmienna promienDoKwadratu nie istnieje, // wiec kompilator zaprotestuje! System.out.println( "Promien kola do kwadratu wynosi: " + promienDoKwadratu ); } public static double obliczPoleKola(int promienKola) { // zmienna promienDoKwadratu dostepna jest od ponizszej linii double promienDoKwadratu = promienKola * promienKola; return 3.14 * promienDoKwadratu; } // metoda sie konczy, zmienna promienDoKwadratu przestaje istniec }
Kolejne pytanie: czy możemy użyć w main argumentu promienKola? Nie – efekt będzie taki sam, jak przy próbie użycia zmiennej promienDoKwadratu. Obie te zmienne to zmienne lokalne.
Czas życia zmiennych lokalnych¶
W poprzednim rozdziale dowiedzieliśmy się, że zmienne lokalne są dostępne jedynie w metodach, w których zostały utworzone.
Za każdym razem, gdy wywołujemy metodę, zmienne lokalne tworzone są od nowa.
Jaki, w takim razie, będzie wynik działania poniższego programu? Czy zobaczymy na ekranie liczby 10 i 20, czy 10 i 30?
public class CzasZyciaZmiennychLokalnych { public static void main(String[] args) { testowaMetoda(10); testowaMetoda(20); } public static void testowaMetoda(int liczba) { int zmiennaLokalna = 0; zmiennaLokalna = zmiennaLokalna + liczba; System.out.println(zmiennaLokalna); } }
Na ekranie zobaczymy liczby 10 i 20 – co prawda dodajemy do lokalnej zmiennej o nazwie zmiennaLokalna wartość przesłanego do metody argumentu, ale po zakończeniu metody, zmienna zmiennaLokalna przestaje istnieć i jej wartość przestaje być dostępna – po ponownym uruchomieniu metody testowaMetoda, zmienna zmiennaLokalna tworzona jest po raz kolejny i "nie pamięta", że przy okazji poprzedniego wywołania przypisaliśmy jej wartość 10.
Podsumowanie zakresu i wywoływania metod, zmiennych lokalnych¶
- Metody w klasach nie muszą być zdefiniowane w kolejności użycia – poniższy kod działa poprawnie – metoda wypiszSume nie musi być przed metodą main:
public class WypiszSume { public static void main(String[] args) { wypiszSume(100, 200); wypiszSume(-5, -20); wypiszSume(0, 0); } public static void wypiszSume(int a, int b) { System.out.println(a + b); } }
- Gdy wywołujemy metodę, to skaczemy do niej z miejsca, gdzie do tej pory odbywało się wykonywanie programu. Poniższy przykład obrazuje kolejność wykonywania kodu programu, w którym używanych jest kilka metod:
- Zmienne lokalne to zmienne utworzone w ciele metody.
- Zmienne lokalne są lokalne dla metody, w której zostały zdefiniowane i są niedostępne nigdzie indziej w programie poza ciałem tej metody.
- Poniższy przykład obrazuje lokalność zmiennych – kompilacja programu zakończy się błędem, ponieważ w metodzie main próbujemy odnieść się do zmiennej promienDoKwadratu, która jest lokalna (istnieje tylko) w metodzie obliczPoleKola:
public class PoleKolaZBledem { public static void main(String[] args) { System.out.println( "Pole kola o promieniu 2 wynosi " + obliczPoleKola(2) ); System.out.println( "Promien kola do kwadratu wynosi: " + promienDoKwadratu ); } public static double obliczPoleKola(int promienKola) { double promienDoKwadratu = promienKola * promienKola; return 3.14 * promienDoKwadratu; } }
- Zmienne lokalne "żyją" w ramach metody, w której zostały utworzone. Za każdym razem, gdy wywołujemy metodę, zmienne lokalne tworzone są od nowa.
Pytania do zakresu i wywoływania metod, zmiennych lokalnych¶
- Czym są zmienne lokalne?
- Czy możemy z metody abc odwołać się do zmiennej lokalnej zdefiniowanej w metodzie xyz?
- Jaki jest zakres życia zmiennych lokalnych?
- Czy poniższy kod jest poprawny i jak go ewentualnie naprawić?
public class ZakresIWywolywanieMetodPytanie1 { public static void main(String[] args) { System.out.println("Wynik: " + kwadrat); int kwadrat = policzKwadrat(10); } public static int policzKwadrat(int liczba) { return liczba * liczba; } }
- Jaki będzie wynik działania poniższego programu?
public class ZakresIWywolywanieMetodPytanie2 { public static void main(String[] args) { policzKwadrat(10); System.out.println("Wynik: " + wynik); } public static void policzKwadrat(int liczba) { int wynik = liczba * liczba; } }
- Jaki będzie wynik działania poniższego programu – co po kolei zobaczymy na ekranie?
public class ZakresIWywolywanieMetodPytanie3 { public static void main(String[] args) { System.out.println( "Witajcie! Kwadrat 10 to: " + policzKwadrat(10) ); System.out.println("Policzone!"); } public static int policzKwadrat(int liczba) { System.out.println("Liczymy kwadrat liczby " + liczba); return liczba * liczba; } }