Hierarchia klas. Dziedziczenie. Polimorfizm.
public class Ksiazka
{ String tytul;
String autor;
int rok; // rok wydania
String wydawnictwo;
Public Ksiazka( String tytul, String autor, int rok, String wydawnictwo)
{this.tytul= new String(tytul); // żeby utworzyć kopię napisu
.......}
String dajTytul( ) {...}
......
String toString( )
{return "Tytul : "+tytul+" autor : "+autor+" rok wydania : "+rok +" wydawnictwo : "+wydawnictwo; }
......
}
public class Artykul
{ String tytul;
String autor;
int rok; // rok wydania
String czasopismo;
Public Artykul( String tytul, String autor, int rok, String czasopismo)
{this.tytul= new String(tytul); .......}
String dajTytul( ) {...}
......
String toString( )
{return "Tytul : "+tytul+" autor : "+autor+" rok wydania : "+rok +" w czasopismie : : "+czasopismo ; }
......
}
Widać wyraźnie powtórzenia zarówno pól jak i metod. Lepsze rozwiązanie wykorzystujące dziedziczenie.
public class Publikacja
{ String tytul;
String autor;
int rok; // rok wydania
Public Publikacja( String tytul, String autor, int rok)
{this.tytul= new String(tytul); .......}
String dajTytul( ) {...}
......
String toString( )
{return "Tytul : "+tytul+" autor : "+autor+" rok wydania : "+rok ; }
......
}
public class Ksiazka extends Publikacja
{ String wydawnictwo;
Public Ksiazka( String tytul, String autor, int rok, String wydawnictwo)
{ super(tytul,autor,rok); // wywołanie konstruktora nadklasy
this.wydawnictwo= new String(wydawnictwo);}
......
String toString( )
{return (super.toString())+ // dostęp do przesłoniętej metody z nadklasy
" wydawnictwo : "+wydawnictwo; }
......
}
public class Artykul extends Publikacja
{ String czasopismo;
Public Artykul( String tytul, String autor, int rok, String czasopismo)
{super(tytul,autor,rok);
this.czasopismo= new String(czasopismo);}
......
String toString( )
{return (super.toString())+" w czasopismie : : "+czasopismo ; }
......
}
Klasa potomna dziedziczy ( a więc ma do nich dostęp bezpośredni) wszystkie atrybuty z wyjątkiem prywatnych. Nadklasa nic nie wie o swoich podklasach.Każda klasa może ieć najwyżej jedną nadklasę ( nie ma wielodziedziczenia).
Główne korzyści z dziedziczenia ( możliwości tworzenia hierarchii klas) :
pokazanie powiązań międzyklasowych
unikanie powtórzeń
możliwość rozbudowy aplikacji bez modyfikacji klas już działających.
public class Jeden
{ protected int xJeden=1;
public Jeden(int a)
{ xJeden = a;}
public String toString()
{ return " "+ xJeden+" ";}
public void metodaJeden()
{ System.out.println("metodaJeden " + this);}
}
public class Dwa extends Jeden
{ protected int xDwa=2;
public Dwa(int a, int b)
{ super(a); // wywołanie konstruktora klasy nadrzędnej
xDwa = b;}
public String toString( ) // przesłonięcie metody z nadklasy
{ return " "+xDwa+" ";}
public void metodaDwa()
{ System.out.println("metodaDwa " + this);}
public void test()
{ Jeden j=new Jeden(111);
j.metodaJeden(); // dostęp do metody z nadklasy
j.xJeden=111111; // bezpośredni dostęp do pola nadklasy - niezalecany
j.metoda();
Jeden j1=new Dwa(111,222); //użycie obiektu podklasy w miejsce obiektu nadklasy
// rzutowanie w górę - bezpieczne
((Dwa)j1).xDwa=22222222; // przykłady rzutowania w dół - ryzykowne
((Dwa)j1).metodaDwa();
// sprawdzenie rzeczywistego typu obiektu :
// if ( j1 instanceof Dwa ) .......
}
}
public class Trzy extends Dwa
{ protected int xTrzy=3;
public Trzy(int a, int b,int c) //początek łańcucha konstruktorów
{ super(a,b);
xTrzy = c;}
public String toString()
{ return " "+ xTrzy +" ";}
public void metodaTrzy()
{ System.out.println("metodaTrzy " + this);}
public void test()
{ ... }
}
Klasy mogą być pisane przez różnych autorów, więc może się zdarzyć następująca sytuacja :
public class Jeden
{ protected int x=1;
public Jeden(int a)
{ x = a;}
public String toString()
{ return " "+ x+" ";}
public void metoda()
{ System.out.println("metodaJeden " + this);}
}
public class Dwa extends Jeden
{ protected int x=2; //przesłonięcie pola
public Dwa(int a, int b)
{ super(a); // wywołanie konstruktora klasy nadrzędnej
x = b;}
public String toString( ) // przesłonięcie metody z nadklasy
{ return " "+x+" ";}
public void metoda() //j.w.
{ System.out.println("metodaDwa" + this);}
public void test()
{ }
}
public class Trzy extends Dwa
{ protected int x=3;
public Trzy(int a, int b,int c) //początek łańcucha konstruktorów
{ super(a,b);
x = c;}
public String toString()
{ return " "+ x +" ";}
public void metodaTrzy()
{ System.out.println("metoda " + this);}
public void test() // niezalecany dostęp do pól nadklas
{ System.out.println(a //pole klasy Trzy
+" "+super.a //pole klasy Dwa
+" "+((Jeden)this).a);} } //pole klasy Jeden
}
public class Test
{ public Test() { ...}
public void test()
{ Trzy b = new Trzy(11,22,33);
Jeden c=b;
System.out.println(b+" "+b.x); //pole klasy Trzy
System.out.println(b+" "+c.x); //pole klasy Jeden ( wczesne wiązanie)
b.metoda(); // metoda klasy Trzy
c.metoda();// metoda klasy Trzy ( późne wiązanie - polimorfizm )
}
}
Atrybuty statyczne.
Są to pola i metody związane z klasą, a nie z konkretnym obiektem ( static) .
class A
{ static int x;
...
static int f() {...}
void metoda( )
{ x=5; //odwołania wewnątrz definicji klasy
f();
..
}
}
A ob= new A( );
int i=ob.x; //tradycyjne odwołanie
ob.f( );
int j=A.x; // odwołania przez nazwę klasy, bez tworzenia obiektu
A.f( );
Modyfikator final.
- dla klasy public final A {...} // nie wolno tworzyć podklas
- dla pola ( po pierwszym nadaniu wartości nie wolno zmieniać watości pola)
final int x=1; // dla zwykłych pól są dwie możliwości
final int y;
...
y=2;
static final MAX_WART=20; // jest to definicja stałej, wolno tylko tak
- dla parametru metody
void f( final int n) //nie wolno zmieniać wartości parametru
- dla metody
final void f( ) // metoda nie może być przedefiniowana w podklasach
Uwaga: w przypadku zmiennych referencyjnych zakaz dotyczy zmiany referencji, ale pozwala na zmiany wartości pól wskazywanego obiektu.