Rozdział 7 - Metody - Zakres i wywoływanie metod, zmienne lokalne

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":

Nazwa pliku: Rozdzial_03__Zmienne/UzyciePrzedDefinicja.java
public class UzyciePrzedDefinicja {
  public static void main(String[] args) {
    System.out.println("Liczba = " + liczba);

    int liczba = 5;
  }
}

Wynik próby kompilacji:

UzyciePrzedDefinicja.java:3: error: cannot find symbol System.out.println("Liczba = " + liczba); ^ symbol: variable liczba location: class UzyciePrzedDefinicja 1 error

Wróćmy do programu liczącego sumę dwóch liczb:

Nazwa pliku: WypiszSume.java
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):

Nazwa pliku: KolejnoscInstrukcji.java
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:

Prosze podac liczbe: 10 Teraz policzymy kwadrat tej liczby. Kwadrat tej liczby wynosi 100.0

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:

Kolejnosc wykonywania instrukcji

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).
Wywołanie metody powoduje przeskok w kodzie do niej z metody, która ją wywołała.

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):

Nazwa pliku: Rozdzial_03__Zmienne/ObwodPoleKola.java
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:

Nazwa pliku: PoleKola.java
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:

Nazwa pliku: PoleKolaZBledem.java
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:

PoleKolaZBledem.java:5: error: cannot find symbol System.out.println("Promien kola do kwadratu wynosi: " + promienDoKwadratu); ^ symbol: variable promienDoKwadratu location: class PoleKolaZBledem 1 error

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:

Zakresem życia zmiennych utworzonych w metodzie jest ciało metody, w której je zdefiniowaliśmy.

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ć:

Nazwa pliku: PoleKolaZBledem.java
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.

Wkrótce poznamy rodzaj zmiennych inny niż 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?

Nazwa pliku: CzasZyciaZmiennychLokalnych.java
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: Kolejnosc wykonywania instrukcji
  • 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

  1. Czym są zmienne lokalne?
  2. Czy możemy z metody abc odwołać się do zmiennej lokalnej zdefiniowanej w metodzie xyz?
  3. Jaki jest zakres życia zmiennych lokalnych?
  4. 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;
      }
    }
    
  5. 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;
      }
    }
    
  6. 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;
      }
    }
    

Odpowiedzi do pytań

Dodaj komentarz

Twój adres email nie zostanie opublikowany. Nie musisz podawać swojego imienia, e-mailu, ani strony www, aby opublikować komentarz. Komentarze muszą zostać zatwierdzone, aby były widoczne na stronie.