Spis treści
- Rozdział 7 - Metody
- Czym są metody?
- Zakres i wywoływanie metod, zmienne lokalne
- Wartości zwracane przez metody
- Argumenty metod
- Metody typu string
- Dokumentowanie metod
- Podsumowanie, pytania i zadania do argumentów metod i metod typu String
- Przeładowywanie metod
Metody mogą zwracać dowolną wartość, zarówno typu prymitywnego (jak int czy boolean), oraz złożonego (np. String lub tablica liczb rzeczywistych double[]). Metody mogą także nie zwracać żadnej wartości – wtedy, zamiast podawać zwracany typ, używamy słowa kluczowego void. Przykładem takiej metody jest każda napisana przez nas metoda main:
public static void main(String[] args) {
System.out.println("Witaj Swiecie!");
}
Uwaga: zwracany typ (lub słowo kluczowe void) musi poprzedzać nazwę metody – poniższa definicja metody main jest niepoprawna i jej próba kompilacji zakończy się błędem:
// blad! void musi byc zaraz przed main!
void public static main(String[] args) { // blad kompilacji
System.out.println("Witaj Swiecie!");
}
Słowo kluczowe return¶
Metody zwracają wartość poprzez użycie słowa kluczowego return. Użycie słowa kluczowego return natychmiast przerywa metodę i zwraca wartość. Metoda może zawierać więcej niż jedno użycie return, ponieważ możemy uzależnić zwracaną wartość od pewnego warunku:
public class ZwrocWiekszaLiczbe {
public static void main(String[] args) {
int x = ktoraWieksza(100, 200);
int y = ktoraWieksza(-5, -20);
System.out.println("x wynosi " + x); // wypisze 200
System.out.println("y wynosi " + y); // wypisze -5
}
public static int ktoraWieksza(int a, int b) {
if (a > b) {
return a;
} else {
return b;
}
}
}
Zdefiniowana powyżej metoda ktoraWieksza zwraca większą z dwóch liczb. Słowo kluczowe return użyte zostało dwa razy – w zależności od tego, która z liczb jest większa, metoda zwróci albo wartość zapisaną w argumencie a albo b.
Metody, które nie zwracają wartości (tzn. definiują zwracany typ jako void), także mogą zawierać użycie słowa kluczowego return, ale musi po nim od razu następować średnik – może być to przydatne, gdy chcemy zakończyć działanie metody przed wykonaniem wszystkich operacji (np. na podstawie jakiegoś warunku):
public class WypiszWynikDzielenia {
public static void main(String[] args) {
wypiszWynikDzielenia(10, 0);
wypiszWynikDzielenia(25, 5);
}
public static void wypiszWynikDzielenia(int x, int y) {
if (y == 0) {
System.out.println("Nie mozna dzielic przez 0!");
return;
}
System.out.println("Wynik dzielenia: " + (x / y));
}
}
Powyższa metoda wypiszWynikDzielenia ma za zadanie wypisać wynik dzielenia dwóch podanych liczb i nic nie zwraca (void). Na początku metody sprawdzamy, czy dzielnik nie jest równy 0 – jeżeli tak, to wypisujemy na ekran informację, że przez zero dzielić nie można i kończymy działanie metody wypiszWynikDzielenia z pomocą użycia return – w takim przypadku nie dojdzie do dzielenia. Wynik działania tego programu:
Pytanie: co stanie się, jeżeli zdefiniujemy, że metoda ma zwracać wartość, ale nie użyjemy return w tej metodzie?
Spójrzmy na poniższy przykład, w którym metoda kwadratLiczby powinna zwrócić wartość typu int, jednak nie zostało w niej użyte słowo kluczowe return:
public class BrakReturn {
public static void main(String[] args) {
int liczbaDoKwadratu = kwadratLiczby(5);
}
public static int kwadratLiczby(int x) {
int wynik = x * x;
// ups! zapomnielismy zwrocic wynik!
}
}
Próba kompilacji tego programu kończy się następującym błędem:
Jeżeli zdefiniowaliśmy zwracany typ, to nasza metoda musi zwracać jakąś wartość – inaczej kod się nie skompiluje (chyba, że metoda rzuca wyjątek – zapoznamy się z takim przypadkiem w rozdziale o wyjątkach).
Podobnie, jeżeli metoda ma nic nie zwracać (void), a użyjemy w niej return i podamy jakąś wartość, to próba kompilacji takiego programu także zakończy się błędem:
public class VoidMetodaZwracaWartosc {
public static void main(String[] args) {
System.out.println("Witaj Swiecie!");
return 5; // blad kompilacji
}
}
Błąd kompilacji – kompilator informuje nas, że w tej metodzie nie spodziewał się zwracania jakiejkolwiek wartości:
Używanie wartości zwracanych przez metody¶
Wiemy już jak wywoływać metody oraz jak zwracać z nich wartości, ale gdzie w zasadzie możemy użyć wywołania metody i zwracanej przez nią wartości?
Wartość zwracana z metody może być:
- przypisana do zmiennej,
- przesłana jako parametr do innej metody,
- użyta w instrukcji if lub pętli itp.,
- w ogóle nie użyta.
Spójrzmy na kilka przykładów.
Przypisanie wyniku metody do zmiennej¶
Jak już wielokrotnie widzieliśmy w tym rozdziale, wynik działania metody możemy przypisać do zmiennej. Typ zmiennej musi być tego samego typu, co definiowany przez metodę zwracany typ:
public class RezultatMetodyPrzypisanyDoZmiennej {
public static void main(String[] args) {
int kwadrat = podniesDoKwadratu(16);
/* zakomentowana linijka, poniewaz powoduje ona blad kompilacji
nie mozna przypisac liczby do zmiennej typu String:
error: incompatible types: int cannot be converted to String
*/
// String tekst = podniesDoKwadratu(16);
}
public static int podniesDoKwadratu(int liczba) {
return liczba * liczba;
}
}
W powyższym przykładzie przypisujemy wynik wywołania metody podniesDoKwadratu do zmiennej typu int o nazwie kwadrat. Linia z przypisaniem wyniku tej metody do zmiennej typu String jest zakomentowana, ponieważ spowodowałaby ona błąd kompilacji – metoda podniesDoKwadratu definiuje, że będzie zwracać wartość liczbową typu int, a String przechowuje tekst – kompilator zaprotestuje.
Rezultat metody jako argument innej metody¶
Wynik metody możemy nie tylko przypisać do zmiennej, ale podać także jako argument innej metody:
import java.util.Scanner;
public class RezultatMetodyJakoArgumentInnejMetody {
public static void main(String[] args) {
System.out.println("Podaj liczbe, a ja wypisze jej kwadrat:");
System.out.println(podniesDoKwadratu(getInt()));
}
public static int getInt() {
return new Scanner(System.in).nextInt();
}
public static int podniesDoKwadratu(int liczba) {
return liczba * liczba;
}
}
W powyższym przykładzie argumentem metody println jest wynik działania metody podniesDoKwadratu. Jednakże, aby ta metoda zwróciła wartość, najpierw musi zostać wywołana metoda getInt, która pobierze od użytkownika liczbę, która zostanie zwrócona i stanie się argumentem metody podniesDoKwadratu. Gdy metoda podniesDoKwadratu wykona się, zwróci liczbę podniesioną do kwadratu, która stanie się z kolei argumentem metody println, służącej do wypisywania tekstu na ekran.
Kolejność wykonania metod będzie więc następująca:
- Najpierw wykona się metoda getInt.
- Następnie, metoda podniesDoKwadratu.
- Na końcu wykona się println, które wypisze wynik podnoszenia liczby do kwadratu na ekran.
Metoda użyta w instrukcji warunkowej¶
Wyniki metod są często używane jako warunki instrukcji warunkowych:
import java.util.Scanner;
public class RezultatMetodyWInstrukcjiWarunkowej {
public static void main(String[] args) {
System.out.println("Podaj liczbe:");
int podanaLiczba = getInt(); // (1)
// wywolujemy metode, a nastepnie sprawdzamy wynik
// przy pomocy instrukcji warunkowej if
if (czyParzysta(podanaLiczba)) { // (2)
System.out.println("Ta liczba jest parzysta.");
} else {
System.out.println("Ta liczba nie jest parzysta.");
}
System.out.println("Podaj kolejna liczbe:"); // (3)
if (getInt() >= 0) { // (4)
System.out.println("Podales nieujemna liczbe.");
} else {
System.out.println("Podales liczbe ujemna.");
}
}
public static int getInt() {
return new Scanner(System.in).nextInt();
}
public static boolean czyParzysta(int liczba) { // (5)
return liczba % 2 == 0; // (6)
}
}
W powyższym przykładzie dzieje się kilka rzeczy:
- Pobieramy (1) od użytkownika liczbę.
- Jak wiemy, instrukcja warunkowa (2) oczekuje wartość true bądź false – tak się składa, że nasza metoda czyParzysta (5) zwraca wartość typu boolean, a typ ten może mieć jedną z dwóch wartości – właśnie true bądź false. Metoda czyParzysta używaoperatora % (reszta z dzielenia) do sprawdzenia, czy reszta z dzielenia przesłanej w argumencie liczby wynosi 0 i zwraca wynik tego porównania (6).
- Wypisujemy komunikat (3), aby użytkownik podał kolejną liczbę.
- Tym razem (4), nie przypisujemy wyniku metody getInt do żadnej zmiennej, lecz porównujemy wynik działania tej metody (czyli liczbę, którą pobraliśmy od użytkownika) od razu do liczby 0 i wypisujemy komunikat, czy jest ona nieujemna.
Warto tutaj jeszcze dodać, że wynik poniższego porównania:
liczba % 2 == 0 // (6)
wynosi true bądź false i tą wartość możemy zwrócić z metody czyParzysta. Powyższy kod moglibyśmy zapisać w mniej zwięzłej formie jako:
public static boolean czyParzysta(int liczba) {
boolean czyLiczbaJestParzysta;
if (liczba % 2 == 0) {
czyLiczbaJestParzysta = true;
} else {
czyLiczbaJestParzysta = false;
}
return czyLiczbaJestParzysta;
}
czy też:
public static boolean czyParzysta(int liczba) {
if (liczba % 2 == 0) {
return true;
} else {
return false;
}
}
Nieużywanie wyniku metody¶
Wartość zwracana z metody nie musi zostać nigdzie użyta – nie ma takiego wymogu. Czasem po prostu chcemy zignorować zwracaną wartość, bo nie jest nam ona do niczego potrzebna:
import java.util.Scanner;
public class RezultatMetodyNieUzyty {
public static void main(String[] args) {
int liczba = getInt();
// pobieramy od uzytkownika druga liczbe,
// ale nigdzie jej nie uzywamy
getInt();
}
public static int getInt() {
return new Scanner(System.in).nextInt();
}
}
Nieosiągalne ścieżki wykonania i ścieżki bez return¶
Jak wiemy z jednego z poprzednich rozdziałów, gdy metoda definiuje, że będzie zwracać wartość, a nie użyjemy słowa kluczowego return, by zwrócić z niej jakąś wartość, nasz program się nie skompiluje:
public class BrakReturn {
public static void main(String[] args) {
int liczbaDoKwadratu = kwadratLiczby(5);
}
public static int kwadratLiczby(int x) {
int wynik = x * x;
// ups! zapomnielismy zwrocic wynik!
}
}
Spójrzmy na bardziej skomplikowany przykład, w którym uzależniamy zwracaną wartość od pewnego warunku – jaki będzie wynik działania tego programu?
public class ReturnWIfie {
public static void main(String[] args) {
double wynikDzielenia = podzielLiczby(100, 25);
}
public static double podzielLiczby(int x, int y) {
if (y != 0) {
return x / y;
}
}
}
Program w ogóle się nie skompiluje! Powodem jest to, że istnieje taka ścieżka wykonania metody podzielLiczby, w której metoda ta nie zwróci żadnej wartości – jeżeli wartość argumentu y będzie wynosić 0, to metoda nie zwróci wartości.
Kompilator, analizując nasz kod źródłowy, jest w stanie wychwycić takie przypadki i zasygnalizować błąd jeszcze na etapie kompilacji:
Kompilator wskazuje błąd w linijce, która zawiera klamrę zamykającą metodę podzielLiczby. dając znam do zrozumienia, że w tym miejscu spodziewał się zwrotu wartości z metody za pomocą return.
Wartość z metody musi zostać zwrócona w każdej możliwej jej ścieżce wykonania – inaczej program się nie skompiluje (o ile dana ścieżka wykonania nie kończy się rzuceniem wyjątku – w takim przypadku zwrócenie wartości nie jest wymagane, ale o takim przypadku opowiemy sobie więcej, gdy będziemy uczyli się o wyjątkach.)
Nieosiągalny kod¶
Kompilator jest także w stanie wykryć instrukcje w metodzie, które nie mają szansy zostać wykonane, tak jak w poniższym przykładzie:
public class InstrukcjaPoReturn {
public static void main(String[] args) {
int kwadrat = liczbaDoKwadratu(10);
}
public static int liczbaDoKwadratu(int x) {
return x * x;
// linia po return nie ma szansy sie wykonac
System.out.println("Instrukcja po return!");
}
}
Kompilacja tego programu zakończy się poniższym błędem, ponieważ kompilator wykrył, że instrukcja System.out.println("Instrukcja po return!"); nie ma szansy się wykonać – instrukcja return, znajdująca się nad nią, spowoduje zakończenie metody i zwrócenie wartości.
Void, czyli niezwracanie wartości¶
Na koniec rozdziału o zwracanych przez metody wartościach spójrzmy na przypadek szczególny, w którym... metoda nic nie zwraca. Jak już wiemy, w takim przypadku, zamiast podawać zwracany typ przed nazwą metody, używamy słowa kluczowego void.
Pytanie: co się stanie, jeżeli spróbujemy przypisać wynik metody, która nic nie zwraca, do, na przykład, zmiennej?
public class BrakZwracanejWartosciPrzypisanie {
public static void main(String[] args) {
int x = wypiszKomunikat("Wczoraj padal deszcz.");
}
public static void wypiszKomunikat(String komunikat) {
System.out.println("Uwaga - komunikat!");
System.out.println(komunikat);
}
}
Powyższy program w ogóle się nie skompiluje. Skoro nasza metoda wypiszKomunikat nic nie zwraca, to nie powinniśmy próbować przypisać zwracanego przez nią wyniku (ponieważ takowego nie będzie) do zmiennej. Kompilator wyświetli komunikat o błędzie, w którym poinformuje nas, że nie może przypisać "niczego" do zmiennej typu int:
Podsumowanie do zwracania wartości¶
- Metody mogą zwracać wartość typu prymitywnego (np. int) oraz złożonego (np. String).
- Jeżeli metoda ma nic nie zwracać, zamiast podawać zwracany typ używamy słowa kluczowego void. Poniższa metoda main nie zwraca żadnej wartości:
public static void main(String[] args) { System.out.println("Witaj Swiecie!"); } - Zwracany typ musi zawsze poprzedzać nazwę metody – poniższy kod jest niepoprawny:
// blad! void musi byc zaraz przed main void public static main(String[] args) { System.out.println("Witaj Swiecie!"); } - Aby zwrócić wartość z metody, używamy słowa kluczowego return, po którym następuje wartość, którą chcemy zwrócić.
- Użycie słowa kluczowego return natychmiast przerywa metodę i zwraca wartość.
- Metoda może zawierać więcej niż jedno użycie return, ponieważ możemy uzależnić zwracaną wartość od pewnego warunku:
public static int ktoraWieksza(int a, int b) { if (a > b) { return a; } else { return b; } } - Metody, które nic nie zwracają (void) także mogą używać słowa kluczowego return, aby przerwać działanie metody:
public static void wypiszWynikDzielenia(int x, int y) { if (y == 0) { System.out.println("Nie mozna dzielic przez 0!"); return; } System.out.println("Wynik dzielenia: " + (x / y)); } - Jeżeli metoda definiuje, że będzie zwracała wartość, a nic z niej nie zwrócimy, to nasz program się nie skompiluje:
public static int kwadratLiczby(int x) { int wynik = x * x; // ups! zapomnielismy zwrocic wynik! blad kompilacji } - Wartość zwracana przez metodę może być:
- Przypisana do zmiennej:
int kwadrat = podniesDoKwadratu(16);
- Przesłana jako parametr do innej metody:
System.out.println(podniesDoKwadratu(getInt()));
- Użyta w instrukcji if lub pętli itp.:
if (czyParzysta(podanaLiczba)) { System.out.println("Ta liczba jest parzysta."); } else { System.out.println("Ta liczba nie jest parzysta."); } - W ogóle nie użyta:
public class RezultatMetodyNieUzyty { public static void main(String[] args) { int liczba = getInt(); // pobieramy od uzytkownika druga liczbe, // ale nigdzie jej nie uzywamy getInt(); } public static int getInt() { return new Scanner(System.in).nextInt(); } }
- Przypisana do zmiennej:
- Jeżeli metoda definiuje, że nie będzie nic zwracać (void), to nie możemy jej przypisać do zmiennej, ani użyć w instrukcji if, pętli itp. – poniższy kod się nie skompiluje:
public class BrakZwracanejWartosciPrzypisanie { public static void main(String[] args) { // blad – probujemy przypisac wynik metody do zmiennej, // a metoda ta nie zwraca zadnej wartosci (void) int x = wypiszKomunikat("Wczoraj padal deszcz."); } public static void wypiszKomunikat(String komunikat) { System.out.println("Uwaga - komunikat!"); System.out.println(komunikat); } } - Jeżeli metoda ma wiele ścieżek wykonania (np. używamy w niej instrukcji if), to w każdej możliwej ścieżce wykonania tej metody musi znajdować się instrukcja return (wyjątkiem od tej reguły jest użycie wyjątków, o których będziemy się wkrótce uczyć). Poniższa metoda spowodowałaby, że program by się nie skompilował, ponieważ metoda nie zwróci żadnej wartości, gdy y == 0:
public static double podzielLiczby(int x, int y) { if (y != 0) { return x / y; } // brakuje return w przypadku, gdy y == 0! blad kompilacji } - Jeżeli po instrukcji return będą znajdować się jakieś instrukcje, to kod takiej metody się nie skompiluje, gdyż kompilator jest w stanie wychwycić takie przypadki i zaprotestować:
public static int liczbaDoKwadratu(int x) { return x * x; // linia po return nie ma szansy sie wykonac – blad kompilacji System.out.println("Instrukcja po return!"); }
Pytania do zwracania wartości¶
- Jak zwrócić z metody wartość?
- Czy metoda, która zwraca liczbę, może także zwrócić tekst (String)?
- Czy metoda może nic nie zwracać, a jeśli tak, to jak to osiągnąć?
- Czy możemy użyć słowa kluczowego return więcej niż raz w metodzie?
- Czy możemy użyć słowa kluczowego return w metodzie, która nic nie zwraca?
- Jeżeli metoda ma zwrócić wartość typu double, ale nie użyjemy w niej return, czy kod się skompiluje?
- Gdzie możemy użyć wartości zwracanej przez metodę?
- Czy wartość zwrócona przez metodę musi zostać zawsze użyta?
- Czy poniższy kod jest poprawny (kod klasy opakowującej metody został pominięty)?
public static void main(String[] args) { String tekst = podniesDoKwadratu(16); } public static int podniesDoKwadratu(int liczba) { return liczba * liczba; } - Które z poniższych metod są nieprawidłowe i dlaczego?
public static wypiszKomunikat() {
System.out.println("Witajcie!");
}
public static void x() {}
public static void getInt() {
return new Scanner(System.in).nextInt();
}
public static int doKwadratu(int c) {
int wynik = c * c;
}
public static int podzielLiczby(int a, int b) {
if (b == 0) {
return;
}
return a / b;
}
public static String getInt() {
return new Scanner(System.in).nextInt();
}
public static int ktoraNajwieksza(int a, int b, int c) {
if (a > b) {
if (a > c) {
return a;
}
} else {
if (c > b) {
return c;
} else {
return b;
}
}
}
public static void wypiszKwadrat(int a) {
System.out.println("Kwadrat wynosi: " + a * a);
return;
System.out.println("Policzone!");
}
public static void wypiszPowitanie {
System.out.println("Witajcie!");
}
Zadania do zwracania wartości¶
Metoda podnosząca do sześcianu¶
Napisz metodę, która zwróci liczbę przesłaną jako argument podniesioną do sześcianu.
Metoda wypisująca gwiazdki¶
Napisz metodę, która wypisze podaną liczbę gwiazdek (znak *) na ekran.