Powoli powoli, bez technicznego podejścia implementacyjnego. Najpierw sobie wyjaśnij pojęcia:
skąd wiadomo co robi metoda getSkok() skoro jest ona zdefiniowana w klasie Rower a obiekt apple jest typu Pojazd
Nie masz OBIEKTU apple. Masz zmienną o nazwie apple. Ta zmienna NIE JEST obiektem. To jest... to jest tylko referencja. Referencja która wskazuje na "coś". Taki link.
Rower ukauka = new Rower(20, 3.0);
Rower a = ukauka;
Pojazd b = ukauka;
Object c = ukauka;
....
Ważne: po takiej serii nie masz czterech obiektów. Masz JEDEN. Tylko jeden stworzyłeś operatorem new, i tylko on gdzieś siedzi. On o sobie wszystko wie - no przecież sam go zrobiłeś operatorkiem new. Więc wie, która metoda ma co wołać. Skąd wie? A jego broszka i jego problem, krzyż mu na drogę. Problemu z tym wielkiego na dobrą sprawę nie ma, bo przecież zrobiłeś new Rower - a w definicji klasy Rower ładnie opisałeś. Że potem porobiłeś jakieś a,b,c... - to go nie interesuje.
No dobra - no to co to są te a,b,c ? No referencje - linki tylko. Wszystkie prowadzą w jedno miejsce. Do tego jednego obiektu Rower, do tego samego, do którego ukauka prowadzi...
a.getSkok(); // zwróci 3.0
b.setSkok( b.getSkok() + 2 ); // właśnie ustawiłeś na 5.0
ukauka.getSkok(); // zwróci 5.0
Bo cały czas to na jednym pracuje obiekcie tym samym.
No to pytanie, co ten interfejs w ogóle? No interfejs to w sumie nic nie robi technicznie, on tylko pilnuje, żebyś w zmiennej "b" trzymał referencję do obiektów, które mają ten getSkok() i tyle.
Zwróc uwagę na zmienną c. Jako Object zadeklarowane. Czyli co? Czyli wiemy, że c wskazuje na coś co jest jakiegoś typu - cholera wie jakiego... jedno jest pewne, że klasyczne metody obiektu obsługuje. Czyli w zasadzie nie możesz nic sensownego zrobić, najwyżej c.toString() albo badziewne c.toHashCode()... ale czy na pewno ? Przecież w tej konkretnej chwili to nasze "c" wskazuje na ten twój rower - cały czas ten jeden obiekt, który sam o sobie to przecież dobrze wie, jakiej jest klasy. I co? I okazuje się, że możesz spokojnie na nim zrobić coś takiego:
int wynik = (Integer)c.getClass().getMethod("getSkok").invoke(c); // i masz piękne 5.0
Dobra, meritum, bo na 10 sposobów mam nadzieję wyjaśniłem, że masz jeden rower i w cholerę zmiennych prowadzących do tego samego rowerka...
Najprościej więc odpowiadając na pytanie dosłownie:
-
apple jest linkiem, i jako link wie dokąd prowadzi. Naprawdę tylko tyle wie. Wie, że prowadzi do czegoś w pamięci. Nic więcej. Jedna liczba. Adres.
-
A to coś w pamięci wie, że jest rowerem - i tylko tyle mu potrzeba, żeby uruchomić co trzeba.
-
cała akcja z deklaracjami, interfejsami i innymi cudami w javie służy tylko temu, żebyś nie mógł przez pomyłkę do zmiennej "b" przypisać czegoś złego. Znaczy masz gwarancję, że to "coś-wskazywane-przez-b" w swojej deklaracji zadeklarowało że obsługuje metody o nazwach getSkok i getCosTam...
-
gwarancja ta wynika stąd, że (patrz pierwsza część punktu (3)) kompilator nie pozwoli na jawne przypisanie czegoś, co jest nieobsługiwane
-
w C++ na przykład to faktycznie cuda się dzieją, i tam to jest x rodzajów wskaźników i cuda niemożliwe. W javie nie. Jeśli ktoś mówi, że w Javie interfejsy to coś więcej, że tam coś się dzieje magicznego... jest w błędzie. Ja jestem złośliwy - pisałem funkcje modyfikujące w locie pola "final", napisanie funkcji która robi przypisanie niezgodne z typem to również żaden problem. I dopiero w trakcie wywołania metody by się wszystko wypieprzyło błędem mniej lub bardziej spektakularnym.