3. Definiowanie operacji na obiektach (metody)
Zestaw operacji na obiektach określany jest przez definicję metod klasy.
Pojęcie metody zbliżone jest do znanego już nam pojęcia funkcji.
Metoda - tak samo jak funkcja - to wyodrębniony zestaw czynności, zapisywany
jednorazowo w postaci fragmentu kodu, który może być wywoływany wielokrotnie
z innych miejsc programu.
Metody służą głównie (ale nie tylko i niekoniecznie) do wykonywania operacji na obiektach.
Zatem - w odróżnieniu od funkcji - metody zwykle wywoływane są na rzecz konkretnych obiektów.
Wywołania "na rzecz" obiektu (jak już widzieliśmy) dokonuje się za pomocą
"operatora" kropka. Np. jeśli p - oznacza obiekt klasy Para (czyli jest referencją
do obiektu klasy Para), a w klasie tej zdefiniowano metodę show, to wywołanie
tej metody na rzecz tego obiektu zapisujemy jako:
p.show();
"Wywołanie na rzecz obiektu" oznacza to samo, co "posłanie polecenia do obiektu"
lub "komunikatu do obiektu" lub "wykonanie operacji na obiekcie".
W tym przypadku (dla metody show):
wywołanie metody show na rzecz obiektu p
=
posłanie komunikatu/polecenie show do obiektu p
=
wykonanie operacji uwidocznienia obiektu p
Schematyczna postać definicji metody jest następująca:
[ specyfikator_dostępu] typ_wyniku nazwa_metody( lista_parametrów ) {
// ... instrukcje wykonywane po wywołaniu metody
}
Uwagi:
- nawiasy kwadratowe oznaczają opcjonalność
- kod zawarty pomiędzy nawiasami klamrowymi nazywany jet ciałem metody
Specyfikator dostępu określa czy metoda może być wywołana spoza klasy, w której jest zdefiniowana. W szczególności:
- specyfikator public mówi o tym, że dana metoda może być wywołana z dowolnej innej klasy
- a private - oznacza, że metoda może być wywołana tylko w tej klasie, w której została zdefiniowana
Nazwę metody zaczynamy od małej litery, stosując dalej notację węgierską,
np. count, setPrice, getAuthor
Te metody, które chcemy udostępnić jako ogólniedostępne
operacje na obiektach oznaczamy słowem public; metody "robocze", które
mają znaczenie tylko dla nas (twórców klasy) i nie powinny być dostępne dla innych użytkowników klasy - oznaczamy słowem private.
Lista parametrów zawiera rozdzielone przecinkami deklaracje parametrów,
które metoda otrzymuje przy wywołaniu. Lista może być pusta (brak argumentów).
Metoda może zwracać wynik (wtedy w jej definicji musimy podać konkretny typ
wyniku, a zakończenie działania metody powinno następować na skutek instrukcji
return zwracającej dane podanego typu). Jeśli metoda nie zwraca żadnego wyniku to jej typ wyniku określamy słowem kluczowym void, a metoda może skończyć działanie na skutek dobiegnięcia do zamykającego nawiasu klamrowego lub wykonania instrukcji return bez argumentów
Instrukcja return ma postać:
return [ wyrażenie ];
Np. metoda zwracająca sumę dwóch liczb całkowitych może wyglądać tak:
int suma(int x, int y) {
int z = x + y;
return z;
}
lub tak
int suma(int x, int y) {
return x + y;
}
Przy wywołaniu metoda suma uzyskuje dwa przekazane jej argumenty
jako parametry x i y. Jej działanie polega na dodaniu obu wartości parametrów
i zwróceniu (do miejsca wywołania) wyniku. Obowiązkowo, w definicji metody
trzeba było podać typ zwracanego wyniku.
Przykład innej metody:
void say(String s) {
System.out.println(s);
}
 Wywołanie metody say spowoduje wyprowadzenie na
konsolę przekazanego jako argument napisu. Metoda nie zwraca żadnego wyniku,
mimo to trzeba było określić typ wyniku słowem kluczowym void (dokładnie "nie dotyczy", znaczy - brak wyniku).
W Javie argumenty przekazywane są metodom wyłącznie przez wartość.
Oznacza to, że w samej metodzie odwołujemy się nie do faktycznego argumentu,
ale do jego kopii. Zatem zmiany przekazanego metodzie argumentu są lokalne,
dotyczą wyłącznie kopii i nie dotykają oryginału.
Np. po wywołaniu metody:
void incr(int x) {
++x;
}
ze zmienną z = 1 jako arguementem w samej metodzie zmienna (parametr) x
uzyska wartość 2, ale po zakończeniu działania metody i powrocie sterowania
do punktu wywołania zmienna z będzie miała nadal wartość 1.
To samo dotyczy typów obiektowych. Pamiętamy: zmienne oznaczające obiekty zawierają referencje, a nie same obiekty.
Zatem np. w ew. metodzie przestawPary:
void przestawPary(Para p1, Para p2) {
Para temp = p1;
p1 = p2;
p2 = temp;
}
nie uzyskamy zamierzonego rezultatu, bowiem metoda otrzymuje tylko wartości
referencji, a nie odniesienia do nich i wszelkie operacje na tych referencjach
dotyczą kopii oryginałów.
Nie znaczy to jednak, że w metodach nie możemy działać na obiektach. Referencje
przecież na nie wskazują: trzeba zatem - poprzez nie - odwoływać się do pól
i metod klasy i za ich pomocą (jeśli jest to możliwe) zmieniać obiekty. O czym dalej.
W klasie mogą być definiowane metody o tej samej nazwie, ale różniące się liczbą i/lub typami argumentów.
Nazywa się to przeciążaniem metod.
Po co taka możliwość?
Wyobraźmy sobie, że na obiektach klasy par liczb całkowitych chcielibyśmy wykonywać operacje:
- dodawania innych obiektów-par
- dodawania (do składników pary) kolejno dwóch podanych liczb cłakowitych
- dodawania (do każdego składnika pary) jednej i tej samej podanej liczby całkowitej
Gdyby nie było przeciążania metod musielibyśmy dla każdej operacji wymyślać
inną nazwę metody. A przecież istota operacji jest taka sama (wystarczy więc
nazwa add), a jej użycie powinno być jasne z kontekstu (określanego przez argumenty).
Dzięki przeciążaniu można w klasie Para np. zdefiniować metody:
void add(Para p) // dodaje do pary, na rzecz której wywołano metodę, parę
// podaną jako argument
void add(int i) // do obu składników pary dodaje podaną liczbę
void add(int i, int k) // pierwszą podaną liczbę dodaje do pierwszego składnika pary
// a drugą - do drugiego
i użyć - gdzie indziej - w naturalny sposób:
Para p;.
Para jakasPara;
....
p.add(3); // wybierana jest ta metoda, która pasuje (najlepiej) do argumentów
p.add(1,2);
p.add(jakasPara);
Identyfikatory metod definiowanych w klasie muszą być od siebie różne.
Wyjątkiem od tej reguły są metody przeciążone tj. takie, które mają tę samą nazwę (identyfikator), ale różne typy i/lub liczbę argumentów
|