29 kwietnia 2008

SCJP - Przeciążanie metod

Kolejny zastrzyk wiedzy przed egzaminem SCJP – kontynuacja dzieła zapowiedzianego w artykule "Przygotowania do SCJP czas zacząć". Dziś będzie o przeciążaniu metod (ang. overloading).

Przeciążanie metod to definiowanie wielu metod o tej samej nazwie a różniących się listą argumentów. W zasadzie o metodzie przeciążającej – tj. używającej tej samej nazwy, co już istniejąca metoda – można myśleć jak o metodzie zupełnie nowej. Nie ma więc żadnych zasad ograniczających zwracany typ, deklarowane wyjątki czy modyfikatory dostępu, jako to ma miejsce w przypadku redefiniowania (ang. overriding). W zasadzie można by to sformułować następująco: jeśli chcemy zdefiniować metodę która będzie się nazywała tak samo jak inna, już istniejąca metoda to musimy określić inną listę argumentów. To tyle.

Mamy więc w naszej klasie zdefiniowanych kilka metod o tej samej nazwie, skąd więc wiadomo, którą z nich wywołujemy w danej chwili? Naturalnie, na podstawie listy parametrów, tyle, że nie zawsze sprawa jest bardzo prosta. Weźmy dla przykładu następujący program.

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

System.out.println(obj.getString(1));
}

String getString(float f) {
return "float value: " + f;
}

String getString(long l) {
return "long value: " + l;
}
}
Jaki jest efekt jego działania? Która z metod getString() zostanie wywołana? W tym wypadku efektem działania programu będzie napis "long value: 1". Autorzy książki nie podali w rozdziale drugim precyzyjnych reguł, ale intuicyjnie, wywoływana jest ta metoda, której argumenty "bardziej odpowiadają" przekazywanym parametrom. Szczegóły mają się pojawić w dalszych rozdziałach i póki co nie będę wybiegał przed szereg – poczekam. Popatrzmy jednak na jeszcze jeden przykład.

class Parent { }

class Child extends Parent { }

class Test {
public static void main(String[] args) {
Parent obj = new Child();

test(obj);
}

static void test(Parent p) {
System.out.println("method for Parent");
}

static void test(Child c) {
System.out.println("method for Child");
}
}
Efektem działania będzie napis "method for Parent", mimo, że obiekt, dla którego wywołano metodę test() jest klasy Child. Powód jest prosty – to, która z przeciążonych metod zostanie wywołana jest określane w czasie kompilacji, kiedy to nie wiadomo jeszcze, jaki będzie rzeczywisty obiekt, a więc o wywoływanej metodzie decyduje typ referencji nie zaś typ obiektu.

1 komentarz:

koziołek pisze...

Wiadomo o co chodzi w pierwszym przykładzie. Kompilator jak i JVM najpierw stara się sprowadzić liczbę 1 do inta. Następnie szuka pierwszej pasującej metody. Nie ma dla int więc w kolejnym kroku szukamy najlepiej pasującej (long), a potem (ustalone eksperymentalnie) jest float, double. Short jako mniej dokładny wymaga rzutowania podobnie char.