6 maja 2008

SCJP – Metody statyczne i przysłanianie metod

W artykule "SCJP - Redefiniowanie metod" pisałem o nadpisywaniu (ang. overriding) metod, choć wtedy nazwałem to trochę nie fortunnie redefiniowaniem. Dziś będzie o przysłanianiu (ang. redefinition), co chciawszy przetłumaczyć wprost z angielskiego mogłoby być nazwane właśnie redefiniowaniem, ale pozostanę przy przysłanianiu – wydaje mi się, że jest to bardziej jednoznaczne określenie.

Metody i zmienne statyczne nie przynależą do obiektu, przynależą one do klasy, z tego też względu powinny być wywoływane względem klasy, ale język Java dopuszcza także składnię, gdzie metodę statyczną wywołujemy względem zmiennej referencyjnej, a więc tak jakby względem obiektu. Poeksperymentujmy trochę z tą koncepcją. Pierwszy przykład poniżej.

class SomeClass {
static void printMessage() {
System.out.println("my message");
}
}

public class Test {
public static void main(String[] args) {
SomeClass obj = null;

obj.printMessage();
}
}

Jaki będzie rezultat uruchomienia tego programu? NullPointerException? Otóż nie! Rezultatem będzie wypisanie napisu "my message". Dzieje się tak dlatego, że kompilator w miejsce zmiennej referencyjnej podstawia dla statycznych odwołań klasę tejże zmiennej. Instrukcja ‘obj.printMessage()’ z powyższego przykładu jest de facto ekwiwalentem dla ‘SomeClass.printMessage()’. Nic więc dziwnego, że program wykona się poprawnie. Podkreślę jeszcze to, co powiedziałem być może nie dość wyraźnie – podstawiany jest typ zmiennej referencyjnej, a nie typ obiektu na jaki ta zmienna ewentualnie wskazuje. Wydaje się to stwierdzenie być dosyć trywialnym po tym jak powyżej pokazałem przykład, gdzie obiektu w ogóle nie było – do zmiennej obj przypisałem null – ale równie trywialne błędy mogą pozbawić nas punktów na egzaminie do SCJP.

Metody statycznej nie można nadpisać (ang. override), ale można ją przesłonić (ang. redefine). Przesłanianie metody, to definiowanie w podklasie metody statycznej, która została już zdefiniowana w nadklasie. Jeśli chodzi o kryteria, jakie musi spełniać metoda by być metodą przysłaniającą to są one takie same jak dla metod nadpisujących i opisałem je w artykule wspomnianym na początku, naturalnie z tym wyjątkiem, że dotyczy to metod statycznych. Aby zrozumieć różnicę, między nadpisywaniem a przesłanianiem przeanalizujmy poniższy przykład.

class Parent {
static String getDescr() {
return "parent";
}
}

class Child extends Parent {
static String getDescr() {
return "child";
}
}

public class Test {
public static void main(String[] args) {
Parent obj = new Parent();
System.out.println(obj.getDescr());

obj = new Child();
System.out.println(obj.getDescr());
}
}

Rezultatem tego programu będzie dwukrotne wypisanie napisu "parent". Tak jak pisałem powyżej, dla odwołań statycznych w miejsce zmiennej referencyjnej podstawiana jest klasa tej zmiennej, a więc w obu przypadkach Parent. Instrukcja ‘obj.getDescr()’ jest więc równoważna instrukcji ‘Parent.getDescr()’ niezależnie od tego, na jaki obiekt wskazuje zmienna ‘obj’. I to jest właśnie przysłanianie. Usuńmy natomiast słówka static z deklaracji metod getDescr(). Tym razem program wypisze "parent" a potem "child". I to jest właśnie nadpisywanie.

4 komentarze:

Anonimowy pisze...

a co się stanie jeśli do przesłanianej metody statycznej dodamy kwalifikator final? sprawa będzie wyglądała tak jak z nadpisywaniem?

Anonimowy pisze...

Ubiegłeś mnie Mariusz. Lecę od końca po wpisach o scjp i dopisuje jeśli czegoś brakuje. Niestety znowu sie pomyliłeś "redefine" to redefinicje, "override" to przysłanianie, przełądowanie...

Mariusz Lipiński pisze...

Dzięki za potężną dawkę komentarzy. Masz racje, moje tłumaczenia terminów redefine, overload i override nie są najszczęśliwsze. Już zresztą była na ten temat dyskusja pod jednym z postów.

BTW - czyżbyś miał właśnie zdawać SCJP i zdecydowałeś się na szybką powtórkę przed egzaminem?

Anonimowy pisze...

chciawszy... wtf?