Wstawianie pól o tej samej nazwie do klas dziedziczących jest bez sensu ponieważ nie jest to jak w przypadku metody "zmiana" (czyli przesłonięcie), lecz dodanie nowego pola (przeciążenie). U Ciebie pierwsze pole nazywa się Unit.attack
, a następne Unit1.attack
lub Unit2.attack
. Oznacza to, że w Unit1
masz dwa pola: Unit.attack
(niedostępne) i Unit1.attack
. Pierwsze z nich nie jest przez Ciebie jawnie zainicjowane, więc robi to za Ciebie kompilator inicjując pole Unit.attack
wartością 0. Stąd taka zwracana wartość. Drugie pole jest już jawnie inicjowane wartością 2 (lub 4) - i taką wartość dostaniesz jeżeli tego pola użyjesz. Gdy w metodzie użyjesz nazwy pola bez kwalifikatora klasy, to domyślnie brana jest klasa, w której znajduje się ta metoda. Dlatego w metodzie Unit.getAttack
dostawałeś wartość 0 (z Unit.attack
).
Gdy przesłoniłeś (jak piszesz) metodę getAttack
w klasie Unit1
lub Unit2
, to otrzymałeś z ich wywołania wartości 2 lub 4 bo odwołują się one do zupełnie innych pól: Pierwsze z klasy bazowej, a drugie z dziedziczącej.
Jako taki przykład podał Ci po wielu edycjach Robcio. Tyle, że w nim jedna metoda publiczna odwołuje się do tego samego pola (bo protected
), tylko inaczej inicjowanego w klasach dziedziczących. Ale częściej używanym rozwiązaniem jest coś odwrotnego - metodę z klasy publicznej przesłania się wersjami z klas dziedziczących i wtedy ta dziedziczona wersja zwraca sobie te inne wartości. Wtedy pole attack
może być prywatne, albo wręcz nie musi istnieć - będzie to prywatna sprawa każdej klasy co sobie jej metoda (lub jej lokalnie przesłonięta wersja) zwróci (np. stałą liczbę 2 lub 4, bez odwoływania się do pól).