Razgovor o Java polimorfizmu
"Sanmej, dosad smo razgovarali o Java enkapsulaciji i nasleđivanju, a danas ćemo nastaviti s polimorfizmom — poslednjom od tri glavne osobine Jave. Kad je savladaš, biće ti sasvim lako da uhvatiš korak s Java objektno-orijentisanim programiranjem", rekao sam Sanmej.
"Brate, nastavi."
Java polimorfizam u objektno-orijentisanom programiranju znači da objekti iste klase u različitim situacijama ispoljavaju različita ponašanja i stanja.
- Podklasa nasleđuje polja i metode nadklase, a objekat podklase može direktno koristiti metode i promenljive nadklase.
- Podklasa može iznova implementirati metodu nasleđenu od nadklase, tako da objekat podklase pri pozivu te metode ispoljava drugačije ponašanje.
- Objekat podklase može se dodeliti promenljivoj tipa nadklase, čime se preko reference tipa nadklase poziva metoda redefinisana u podklasi i time ostvaruje polimorfizam.
"Suvo zvuči, zar ne? Hajde da to konkretnije analiziramo."
01,Šta je polimorfizam
U mom sećanju, onaj sjajan okršaj između Sun Wukonga i Erlang Shena iz "Putovanja na zapad" savršeno objašnjava reč "polimorfizam": jedan Sun Wukong može da se preobrazi u sedamdeset dva oblika; jedan Erlang Shen takođe može u sedamdeset dva oblika; obojica se mogu pretvoriti u različite oblike, a dovoljno je da tiho izgovore "promena".
Šta je Java polimorfizam? To je zapravo sposobnost — isto ponašanje ima različite oblike ispoljavanja; drugim rečima, pri izvršavanju određenog koda Java u toku rada, na osnovu razlika među objektima, proizvodi različite rezultate. Isto kao što Sun Wukong i Erlang Shen samo treba da izgovore "promena" i već se pretvore — i to svaki put u nešto drugo; jedan te isti princip.
Tri su uslova za polimorfizam:
- Podklasa nasleđuje nadklasu
- Podklasa redefiniše metodu nadklase
- Referenca nadklase ukazuje na objekat podklase
Jednostavna primena polimorfizma, pogledajmo spisak programa 1-1:
// podklasa nasleđuje nadklasu
public class Wangxiaoer extends Wanger {
public void write() { // podklasa redefiniše metodu nadklase
System.out.println("Zapamtimo mržnju, pokazimo odlučnost da se osnažimo");
}
public static void main(String[] args) {
// referenca nadklase ukazuje na objekat podklase
Wanger[] wangers = { new Wanger(), new Wangxiaoer() };
for (Wanger wanger : wangers) {
// kada je objekat Wanger, ispisuje: Ne zaboravimo sramotu domovine
// kada je objekat Wangxiaoer, ispisuje: Zapamtimo mržnju, pokazimo odlučnost da se osnažimo
wanger.write();
}
}
}
class Wanger {
public void write() {
System.out.println("Ne zaboravimo sramotu domovine");
}
}02,Polimorfizam i kasno povezivanje
Sada da razmislimo o jednom pitanju: kada se u spisku programa 1-1 izvrši wanger.write(), pošto kompajler ima samo jednu referencu tipa Wanger, kako zna da li da pozove metodu write() nadklase Wanger ili metodu write() podklase Wangxiaoer?
Odgovor je — u toku rada, na osnovu tipa objekta, vrši se kasno povezivanje. U fazi prevođenja kompajler ne zna tip objekta, ali mehanizam poziva metoda u Javi pronalazi ispravno telo metode, a zatim ga izvršava i daje tačan rezultat.
Jedna važna prednost koju pruža mehanizam polimorfizma jeste dobra proširivost programa. Pogledajmo spisak programa 2-1:
// podklasa nasleđuje nadklasu
public class Wangxiaoer extends Wanger {
public void write() { // podklasa redefiniše metodu nadklase
System.out.println("Zapamtimo mržnju, pokazimo odlučnost da se osnažimo");
}
public void eat() {
System.out.println("Ne volim da čitam, volim samo da jedem");
}
public static void main(String[] args) {
// referenca nadklase ukazuje na objekat podklase
Wanger[] wangers = { new Wanger(), new Wangxiaoer() };
for (Wanger wanger : wangers) {
// kada je objekat Wanger, ispisuje: Ne zaboravimo sramotu domovine
// kada je objekat Wangxiaoer, ispisuje: Zapamtimo mržnju, pokazimo odlučnost da se osnažimo
wanger.write();
}
}
}
class Wanger {
public void write() {
System.out.println("Ne zaboravimo sramotu domovine");
}
public void read() {
System.out.println("Svake nedelje pročitaj jednu dobru knjigu");
}
}U spisku programa 2-1 dodali smo metodu read() u klasu Wanger i metodu eat() u klasu Wangxiaoer, ali to nimalo ne utiče na poziv metode write().
Metoda write() zanemaruje promene u okolnom kodu i i dalje radi normalno. To me podseća na stihove iz "Devet yang prave pisme" u romanu Džin Junga "Nebeski mač i zmajeva sablja": "Neka je snažan koliko hoće, hladan vetar pošumi po brdu; neka je drsk koliko hoće, sjajna mesecina obasjava reku."
Ova odlična osobina polimorfizma znači da prilikom izmene koda ne moramo biti previše napeti, jer je polimorfizam važna osobina koja programeru omogućava da "razdvoji ono što se menja od onoga što ostaje nepromenjeno".
03,Polimorfizam i konstruktori
Pozivanje polimorfne metode unutar konstruktora daje zanimljiv rezultat. Pogledajmo spisak programa 3-1:
public class Wangxiaosan extends Wangsan {
private int age = 3;
public Wangxiaosan(int age) {
this.age = age;
System.out.println("Uzranja Wangxiaosana: " + this.age);
}
public void write() { // podklasa redefiniše metodu nadklase
System.out.println("Moja treća godina kada krećem u vrtić je: " + this.age);
}
public static void main(String[] args) {
new Wangxiaosan(4);
// Pre polaska u vrtić
// Moja treća godina kada krećem u vrtić je: 0
// Nakon polaska u vrtić
// Uzranja Wangxiaosana: 4
}
}
class Wangsan {
Wangsan () {
System.out.println("Pre polaska u vrtić");
write();
System.out.println("Nakon polaska u vrtić");
}
public void write() {
System.out.println("Ja sam u vrtić krenuo sa tri i po godine");
}
}Da li te iznenada iznenađuje rezultat? Pri kreiranju objekta Wangxiaosan prosleđena je starost 4, ali izlaz nije ni "Ja sam u vrtić krenuo sa tri i po godine", ni "Moja treća godina kada krećem u vrtić je: 4".
Zašto?
Zato što se pri kreiranju objekta podklase najpre poziva konstruktor nadklase, a konstruktor nadklase zatim poziva polimorfnu metodu koju je podklasa redefinisala. Pošto nadklasa ne zna koje su vrednosti polja u objektu podklase, privremeno inicijalizuje svojstvo tipa int na 0, a tek potom poziva konstruktor podklase (konstruktor podklase zna da je starost 4).
04,Polimorfizam i konverzija nadole
Konverzija nadole znači eksplicitno prevođenje reference nadklase u tip podklase; to nije bezbedno, jer referenca nadklase ponekad ukazuje na objekat nadklase, pa će konverzija nadole baciti ClassCastException što znači da je konverzija tipa promašena. Ali ako referenca nadklase ukazuje na objekat podklase, konverzija nadole će uspeti.
Pogledajmo spisak programa 4-1:
public class Wangxiaosi extends Wangsi {
public void write() {
System.out.println("Zapamtimo mržnju, pokazimo odlučnost da se osnažimo");
}
public void eat() {
System.out.println("Ne volim da čitam, volim samo da jedem");
}
public static void main(String[] args) {
Wangsi[] wangsis = { new Wangsi(), new Wangxiaosi() };
// wangsis[1] može da se konvertuje nadole
((Wangxiaosi) wangsis[1]).write();
// wangsis[0] ne može da se konvertuje nadole
((Wangxiaosi)wangsis[0]).write();
}
}
class Wangsi {
public void write() {
System.out.println("Ne zaboravimo sramotu domovine");
}
public void read() {
System.out.println("Svake nedelje pročitaj jednu dobru knjigu");
}
}"Dobro, Sanmej, time smo završili sve tri glavne osobine Jave — enkapsulaciju, nasleđivanje i polimorfizam. Nadam se da ćeš ih ponovo srediti u glavi."
"Dobro, Autor, poslušaću", nasmejala se Sanmej žustro.
