Polimorfizam u Javi. Dinamičko i statičko povezivanje

2

Recimo da nije bilo funkcije hello i da mi jednostavno pozovemo ob.display u osnovi, a onda poziva funkciju prikaza klase B, a ne klase A.

Poziv funkcije display() jednom postavlja kompilator na verziju definiranu u osnovnoj klasi. To se naziva statično razrješenje poziva funkcije ili statičko vezanje - poziv funkcije je fiksiran prije nego što se program izvrši. Ovo se također ponekad naziva ranim vezanjem jer je funkcija display() postavljena tijekom kompajliranja.

Sada kako može pozvati funkciju preslikavanja izvedene klase bez korištenja ključne riječi virtual ( kasni uvez) prije funkcije prikaza u osnovnoj klasi?

Sada u ovom programu prosljeđivanje objekta kao poziva prema vrijednosti, poziva prema pokazivaču i poziva prema referenci na Hello funkciju radi dobro. Sada, ako koristimo polimorfizam i želimo mapirati funkciju članicu izvedene klase ako je pozvana, moramo dodati virtualnu ključnu riječ prije funkcije mapiranja u bazi. Ako proslijedite vrijednost objekta kada se poziva pomoću pokazivača i poziva po referenci, to je poziv funkcije u izvedenoj klasi, ali ako proslijedite objekt po vrijednosti, nije, zašto je to tako?>

Klasa A ( public: void display(); // virtualni void display() ( cout<< "Hey from A" <display() ) int main() ( B obj; Pozdrav(obj); // obj //&ob return 0; )

  • 2 odgovora
  • Sortiranje:

    Aktivnost

4

sada kako može pozvati funkciju preslikavanja izvedene klase bez korištenja ključne riječi virtual (kasno vezanje) prije funkcije preslikavanja u osnovnoj klasi?

Nevirtualnu funkciju prevoditelj jednostavno rješava prema statičkom tipu objekta (ili reference ili pokazivača) koji poziva. Dakle, dati objekt izvedenog tipa, kao i referenca na njegov podobjekt:

Bb; A&a=b;

dobit ćete različite rezultate pozivanjem nevirtualne funkcije:

b.display(); // poziva se kao B a.display(); // naziva se A

Ako znate pravi tip, možete odrediti kako želite nazvati ovu verziju:

statično_cast (a).display(); // naziva se B

ali što bi bilo užasno pogrešno ako objekt a upućuje nije tipa B .

Sada, ako koristimo polimorfizam i želimo mapirati funkciju članicu izvedene klase ako je pozvana, moramo dodati virtualnu ključnu riječ prije funkcije mapiranja u bazi.

Ispraviti. Ako funkciju učinite virtualnom, ona će se razriješiti tijekom izvođenja u skladu s dinamičkim tipom objekta, čak i ako koristite drugu vrstu reference ili pokazivača za pristup. Dakle, oba gornja primjera bi to nazvala B .

Ako proslijedimo vrijednost objekta pozivom po pokazivaču i pozivom po referenci, on poziva funkciju u izvedenoj klasi, ali ako proslijedimo objekt po vrijednosti, to ne znači zašto je to tako?

Ako ga prođete po vrijednosti, onda vi posjekotine njegovo: kopiranje samo A dijela objekta da bi se napravio novi objekt tipa A . Dakle, bez obzira je li ta funkcija virtualna ili nije, njezino pozivanje na tom objektu odabrat će verziju A, budući da je to A i ništa osim A.

0

wikipedia kaže da se rezanje objekta događa jer nema mjesta za pohranjivanje dodatnih članova izvedene klase u superklasi, pa je to rezanje. Zašto se rezanje objekata ne događa ako ga proslijeđujemo referencom ili pokazivačem? Zašto superklasa dobiva dodatni prostor za pohranu? -

Prije nego što se dotaknemo korištenja virtualnih funkcija, potrebno je razmotriti takve koncepte kao što su rano i kasno vezanje. Usporedimo dva pristupa kupnji npr. kilograma naranči. U prvom slučaju unaprijed znamo da trebamo kupiti 1 kg. naranče. Stoga uzimamo mali paket, ne puno, ali dovoljno novca da bude dovoljno za ovaj kilogram. U drugom slučaju, mi, izlazeći iz kuće, ne znamo što i koliko trebamo kupiti. Stoga, uzimamo auto (što ako će biti puno stvari i teških stvari), opskrbljujemo se paketima velikih i malih veličina i uzimamo što više novca. Odemo na tržnicu i ispostavi se da trebamo kupiti samo 1 kg. naranče.

Gornji primjer u određenoj mjeri odražava značenje uporabe ranog, odnosno kasnog vezanja. Očito je za ovaj primjer prva opcija optimalna. U drugom slučaju smo previše predviđali, ali nam nije trebalo. S druge strane, ako na putu do tržnice zaključimo da nam naranče ne trebaju i odlučimo kupiti 10 kg. jabuke, onda u prvom slučaju to više nećemo moći. U drugom slučaju je lako.

Sada razmotrite ovaj primjer s programskog gledišta. Kada koristimo rano vezanje, na neki način kažemo kompajleru: "Znam točno što želim. Stoga, čvrsto (statički) vežite sve pozive funkcija." Kada koristimo mehanizam kasnog povezivanja, na neki način kažemo kompajleru: "Još ne znam što želim. Kad dođe vrijeme, reći ću ti što i kako želim."

Stoga, tijekom ranog povezivanja, pozivatelj i pozvani su vezani prvom prilikom, obično u vrijeme kompajliranja.

Kada kasno vežete pozvanu metodu i pozivnu metodu, one se ne mogu vezati u vrijeme kompajliranja. Stoga je implementiran poseban mehanizam koji određuje kako će pozvane i pozivajuće metode biti vezane kada se poziv stvarno izvrši.

Očito je da je brzina i učinkovitost ranog vezanja veća od one kasnog vezivanja. U isto vrijeme, kasno uvezivanje pruža određenu fleksibilnost uvezivanja.

Konačno smo došli do samih virtualnih funkcija i metoda. Nažalost, prilično je teško povući bilo kakvu analogiju s fizičkim svijetom za ilustraciju virtualnih metoda. Stoga ćemo odmah razmotriti ovo pitanje s gledišta programiranja.

Dakle, za što se koriste virtualne metode? Virtualne metode postoje tako da se "nasljednik" ponaša drugačije od "pretka", zadržavajući svojstvo kompatibilnosti s njim.

Ovdje je definicija virtualnih metoda:

virtualna metoda je metoda koja, kada je deklarirana u potomcima, svugdje zamjenjuje odgovarajuću metodu, čak i u metodama deklariranim na pretku, ako je pozvana na potomku.

Adresa virtualne metode poznata je samo u trenutku izvođenja programa. Kada se pozove virtualna metoda, njena adresa se preuzima iz tablice virtualnih metoda svoje klase. Dakle, zove se ono što je potrebno.

Prednost korištenja virtualnih metoda je u tome što koristi upravo mehanizam kasnog povezivanja, koji omogućuje obradu objekata čiji tip nije poznat u vrijeme prevođenja.

Kako bih ilustrirao korištenje virtualnih metoda, dat ću primjer u jeziku C++ koju sam posudio od jednoga C++ vodič. Čak i ako niste baš dobro upoznati s ovim jezikom, nadam se da ću svojim objašnjenjima barem nekako objasniti njegovo značenje.

#uključi // veza standardne C++ biblioteke, // koja opisuje neke funkcije korištene u programu class vehicle // class "vehicle" ( int wheels; float weight; public: // početak javnog (otvorenog) dijela klase virtual void poruka (void) (cout message(); // poziva metodu poruke brisanja unicycle objekta; // brisanje unicycle objekta // Svi naredni blokovi od 3 reda su apsolutno identični prvom bloku // s jedinom razlikom da se klasa kreiranog objekta // mijenja u auto, kamion, čamac jednocikl = novi automobil; monocikl->poruka(); brisanje jednocikla; jednocikl = novi kamion; jednocikl->poruka(); brisanje jednocikla; monocikl = novi brod ; monocikl->poruka(); brisanje monocikla; )

Rezultati programa (ispis na ekranu):

Vozilo Auto Vozilo Brod

Razmotrimo navedeni primjer. Imamo tri razreda automobil, kamion i čamac, koji su izvedeni iz osnovne klase vozilo. U osnovnoj klasi vozilo opisana je virtualna funkcija poruka. U dva od tri razreda automobil, čamac) također opisuje njegove funkcije poruka, i u razredu kamion nema opisa njegove funkcije poruka. Svi stihovi, koje nisam komentirao, nemaju temeljno značenje za ovaj primjer. Sada idemo preko glavnog bloka programa - funkcija glavni(). Opisivanje varijable monocikl, kao pokazivač na objekt tipa vozilo. Neću ulaziti u detalje zašto baš pokazivač na objekt. Dakle, potrebno je. U ovom slučaju, tretirajte pokazivač kao da je sam objekt. Detalje rada s pokazivačima možete pronaći u opisima pojedinog OOP jezika. Zatim kreiramo objekt klase vozilo, varijabla monocikl pokazuje na ovaj objekt. Nakon toga pozivamo metodu poruka objekt monocikl, au sljedećem retku brišemo ovaj objekt. U sljedeća tri bloka od 3 retka provodimo slične operacije, s jedinom razlikom što radimo s objektima klase automobil, kamion, čamac. Korištenje pokazivača omogućuje nam korištenje istog pokazivača za sve izvedene klase. Zanima nas poziv funkcije poruka za svaki od objekata. Ako nismo naznačili da funkcija poruka razreda vozilo je virtualna ( virtualan), tada bi prevodilac statički (hard) vezao bilo koji poziv metode na objekt pokazivača monocikl s metodom poruka razreda vozilo, jer pri opisivanju rekli smo da varijabla monocikl pokazuje na objekt klase vozilo. Oni. proizvela bi rano vezanje. Izlaz takvog programa bio bi izlaz četiriju linija "Vozilo". No zbog korištenja virtualne funkcije u nastavi dobili smo malo drugačije rezultate.

Pri radu s objektima klase automobil i čamac vlastite metode nazivaju se poruka, što se potvrđuje prikazivanjem odgovarajućih poruka na ekranu. Na satu kamion nikakva metoda poruka, iz tog razloga se poziva odgovarajuća metoda osnovne klase vozilo.

Vrlo često se klasa koja sadrži virtualnu metodu naziva polimorfna klasa. Najvažnija razlika je u tome što polimorfne klase dopuštaju obradu objekata čiji tip nije poznat u vrijeme prevođenja. Funkcije deklarirane u osnovnoj klasi kao virtualne mogu se modificirati u izvedenim klasama, a vezanje se neće dogoditi u fazi kompilacije (ono što se zove rano vezanje), već u trenutku pozivanja metode (kasno vezanje).

Virtualne metode opisuju se pomoću ključne riječi virtualan u osnovnoj klasi. To znači da se u izvedenoj klasi ova metoda može zamijeniti metodom koja je prikladnija za tu izvedenu klasu. Proglašena virtualnom u osnovnoj klasi, metoda će ostati virtualna za sve izvedene klase. Ako virtualna metoda nije nadjačana u izvedenoj klasi, tada će poziv pronaći metodu s tim imenom u hijerarhiji klase (tj. u osnovnoj klasi).

Zadnja stvar o kojoj treba govoriti o virtualnim funkcijama je koncept apstraktnih klasa. Ali to ćemo pogledati u sljedećem koraku.

VIRTUALNE FUNKCIJE_________________________________________________________________ 1

Rani i kasni uvez. Dinamički polimorfizam _______________________________________ 1

Virtualne funkcije ___________________________________________________________________________ 1 Virtualni destruktori __________________________________________________________________ 4 Apstraktne klase i čiste virtualne funkcije ___________________________________________________________ 5

VIRTUALNE FUNKCIJE

Rani i kasni uvez. Dinamički polimorfizam

C++ podržava polimorfizam na dva načina.

Prvo, podržan je pri kompilaciji kroz preopterećenje funkcija i operatora. Ova vrsta polimorfizma naziva se statički polimorfizam, budući da se provodi i prije ovrhe

program, kroz rano povezivanje identifikatora funkcija s fizičkim adresama tijekom kompilacije i povezivanja.

Drugo, tijekom izvođenja programa, on se održava kroz virtualne funkcije. Nakon što u programskom kodu naiđe na poziv virtualne funkcije, prevodilac (ili, točnije, povezivač) samo označava taj poziv, ostavljajući povezivanje identifikatora funkcije s njegovom adresom do faze izvršenja. Takav proces se zove kasni uvez.

Virtualna funkcija je funkcija čiji poziv (i radnje koje izvodi) ovisi o vrsti objekta za koji se poziva. Objekt određuje koja se funkcija treba pozvati tijekom izvođenja programa. Ova vrsta polimorfizma naziva se dinamički polimorfizam.

osnova dinamički polimorfizam je mogućnost koju daje C++ za definiranje pokazivača na osnovnu klasu koja će zapravo pokazivati ​​ne samo na objekt ove klase, već i na bilo koji objekt izvedene klase. Ova sposobnost dolazi od nasljeđivanja, jer je objekt izvedene klase uvijek objekt osnovne klase. U vrijeme kompajliranja još nije poznato koji objekt klase korisnik želi stvoriti, s obzirom na pokazivač na objekt osnovne klase. Takav pokazivač pridružuje se svom objektu samo tijekom izvođenja programa, odnosno dinamički. Klasa koja sadrži barem jednu virtualnu funkciju naziva se polimorfnom.

Za svaki polimorfni tip podataka, prevodilac stvara tablicu virtualnih funkcija i ugrađuje skriveni pokazivač na tu tablicu u svaki objekt takve klase. Sadrži adrese virtualnih funkcija odgovarajućeg objekta. Naziv pokazivača na tablicu virtualnih funkcija i naziv tablice ovise o implementaciji u pojedinom prevoditelju. Na primjer, u Visual C++ 6.0 ovaj se pokazivač zove vfptr, a tablica se zove vftable (Virtual Function Table). Kompajler automatski ugrađuje na početak polimorfnog konstruktora klase dio koda koji inicijalizira pokazivač na virtualnu tablicu funkcija. Ako se pozove virtualna funkcija, kod koji generira prevodilac pronalazi pokazivač na tablicu virtualne funkcije, zatim iterira kroz tu tablicu i iz nje izdvaja adresu odgovarajuće funkcije. Nakon toga se vrši prijelaz na navedenu adresu i poziv funkcije.

Podsjetimo se da kada kreirate objekt izvedene klase, prvo se poziva konstruktor njegove osnovne klase. U ovoj fazi kreira se tablica virtualnih funkcija, kao i pokazivač na nju. Nakon što se pozove izvedeni konstruktor klase, pokazivač tablice virtualne funkcije postavlja se tako da pokazuje na nadjačanu verziju virtualne funkcije (ako postoji) koja postoji za objekt te klase.

U tom smislu morate biti svjesni cijene koju morate platiti za mogućnost korištenja kasnog vezanja.

Budući da objekti s virtualnim funkcijama također moraju održavati tablicu virtualnih funkcija, njihova upotreba uvijek dovodi do povećanja troškova memorije i smanjenja performansi programa. Ako radite s malom klasom za koju ne namjeravate da bude osnovna klasa za druge klase, nema smisla koristiti virtualne funkcije.

Virtualne funkcije

Funkcije koje imaju poznato sučelje poziva (to jest, prototip), ali implementaciju koja se ne može specificirati u općem slučaju, već se može definirati samo za posebne slučajeve, nazivaju se virtualnim (izraz koji znači da se funkcija može ponovno implementirati u izvedena klasa).

Virtualne funkcije su funkcije koje jamče da će biti pozvana ispravna funkcija za objekt, bez obzira koji se izraz koristi za pozivanje.

Pretpostavimo da osnovna klasa sadrži funkciju koja je deklarirana virtualnom, a izvedena klasa definira istu funkciju. U ovom slučaju, funkcija iz izvedene klase poziva se na objekte izvedene klase, čak i ako se poziva pomoću pokazivača ili reference na osnovnu klasu. Primjer:

klasa Koord

Osnovna koordinatna klasa

// osnovna klasa koordinata

zaštićen:

// zaštićeni članovi klase

dvostruko x, y;

// koordinate

javnost:

// javni članovi klase

Koord () (x = 0; y = 0;)

// konstruktor osnovne klase

voidInput();

// deklarira nevirtualnu funkciju

virtualna praznina Ispis();

// deklarira virtualnu funkciju

void Coord::Input()

// omogućuje unos koordinata s tipkovnice

cout<<"\tx=";

// unosi vrijednost x s tipkovnice

cout<<"\ty=";

// unosi vrijednost y s tipkovnice

void Coord::Print()

// prikazuje vrijednosti koordinata na ekranu

cout<<"\tx="<

Klasa izvedena iz točaka

classDot: javnaKoord

// nasljednik klase koordinata

char ime;

// ime točke

javnost:

// javni članovi klase

Točka (znak N) : Koord () ( ime = N ; )

// poziva konstruktor osnovne klase

voidInput();

voidPrint();

void Dot::Input()

// omogućuje unos koordinata točke s tipkovnice

char S = "Unesite koordinate točke";

CharToOem (S , S);

cout<

Koord::Ulaz();

void Dot::Print()

// prikazuje vrijednosti koordinata točke na ekranu

char S ="Koordinate točke ";

CharToOem (S , S);

// pretvara niz znakova u ćirilicu

cout<

// prikazuje naslov i naziv točke

Koord::Ispis();

// poziva funkciju osnovne klase

klasa Vec: publicCoord

Klasa izvedena iz vektora

// nasljednik klase koordinata

char ime [3];

// naziv vektora

javnost:

// javni članovi klase

Vec (char * pName) : Coord () ( strncpy (name , pName , 3) ​​​​; name [ 2 ] = "\0" ; )

voidInput();

// nadjačava nevirtualnu funkciju

voidPrint();

// redefinira virtualnu funkciju

voidVec::Input()

// omogućuje unos vektorskih projekcija s tipkovnice

Predavanje 9 Virtualne funkcije 3

char S = "Unesite vektorske projekcije";// deklarira i inicijalizira prompt niz

CharToOem (S , S);

// pretvara niz znakova u ćirilicu

cout<

// prikazuje upit i naziv vektora

Koord::Ulaz();

// poziva funkciju osnovne klase

voidVec::Ispis()

// prikazuje vrijednosti vektorskih projekcija na ekranu

char S = "Vektorske projekcije";

// deklarira i inicijalizira redak zaglavlja

CharToOem (S , S);

// pretvara niz znakova u ćirilicu

cout<

// prikazuje naslov i naziv vektora

Koord::Ispis();

// poziva funkciju osnovne klase

U gornjem primjeru, deklarirana je osnovna klasa Coord i dvije izvedene klase Dot i Vec. Funkcija Print() u izvedenim klasama je virtualna jer je deklarirana virtualnom u osnovnoj klasi Coord. Funkcija Print() u Dot i Vec izvedenim klasama nadjačava funkciju osnovne klase. Ako izvedena klasa ne pruža nadjačanu implementaciju funkcije Print(), koristi se zadana implementacija iz osnovne klase.

Funkcija Input() proglašena je nevirtualnom u osnovnoj klasi Coord i nadjačana u izvedenim klasama Dot i Vec.

void main()

Coord* pC = new Coord () ;

// deklarira pokazivač na koordinate i dodjeljuje memoriju

Točka*pD = nova točka("D") ;

// deklarira pokazivač na točku i dodjeljuje memoriju

Vec*pV = novi Vec("V") ;

// deklarira pokazivač na vektor i dodjeljuje memoriju

pC->Ulaz() ;

PC->Ispis() ;

// poziva virtualnu funkciju Coord::Print()

// pokazivač na koordinate dobiva adresu objekta točkastog tipa

pC->Ulaz() ;

// poziva nevirtualnu funkciju Coord::Input()

PC->Ispis() ;

// poziva virtualnu funkciju Dot::Print()

// pokazivač na koordinate dobiva adresu objekta vektorskog tipa

pC->Ulaz() ;

// poziva nevirtualnu funkciju Coord::Input()

PC->Ispis() ;

// poziva virtualnu funkciju Vec::Print()

U gornjem primjeru, pokazivač na koordinate pC naizmjenično preuzima vrijednosti adrese objekata koordinata, točke i vektora. Iako se tip pC pokazivača ne mijenja, on poziva različite virtualne funkcije ovisno o svojoj vrijednosti.

Kada se koristi pokazivač na osnovnu klasu koji zapravo pokazuje na objekt izvedene klase, poziva se nevirtualna funkcija osnovne klase.

Treba napomenuti da je operacija dodjele pC = pD, koja koristi operande različitih tipova (Coord* i Dot* ) bez konverzije, moguća samo za pokazivač osnovne klase na lijevoj strani. Obrnuto dodjeljivanje pD = pC je nezakonito i uzrokuje sintaktičku pogrešku.

Kada se izvrši, program prikazuje:

Koordinate točke D:

Vektorske projekcije V:

Pri pozivanju funkcije pomoću pokazivača i referenci primjenjuju se sljedeća pravila:

virtualni poziv funkcije rješava se prema tipu objekta čiju adresu pohranjuje pokazivač ili referenca;

pozivanje nevirtualne funkcije rješava se prema vrsti pokazivača ili reference.

Virtualne funkcije pozivaju se samo za objekte koji pripadaju nekoj klasi. Zato

ne možete proglasiti globalnu ili statičku funkciju virtualnom. Virtualna ključna riječ može

podaci . Cilj polimorfizma, primijenjenog na objektno orijentirano programiranje, je korištenje jednog imena za definiranje uobičajenih radnji za klasu.

U jeziku Java, varijable objekta su polimorfne. Na primjer:
class King ( public static void main(String args) ( King king = new King() ; king = new AerysTargaryen() ; king = new RobertBaratheon() ; ) ) class RobertBaratheon extends King ( ) class AerysTargaryen extends King ( )
Varijabla tipa King može se odnositi na objekt tipa King ili na objekt bilo koje podklase King.
Uzmimo sljedeći primjer:

class King ( javni nevažeći govor() ( System .out .println ("Ja sam kralj Andala!") ; ) javni nevažeći govor(String citat) ( System .out .println ("Mudar čovjek je rekao: " + citat) ; ) javni nevažeći govor(Boolean speakLoudly) ( if (speakLoudly) System .out .println ( "JA SAM KRALJ ANDALA!!!11") ; else System .out .println ("ja" sam... kralj..." ) ; ) ) klasa AerysTargaryen extends King ( @Override public void words() ( System .out .println ("Spalite ih sve... " ) ; ) @Override public void voice(String quotation) ( System .out .println (quotation+ " ... A sada ih sve spali!" ) ; ) ) class Kingdom ( public static void main(String args) ( King king = new AerysTargaryen() ;king.speech("Homo homini lupus est" ) ; ) )
Što se događa kada se pozove metoda koja pripada objektu kralj?
1. Prevodilac provjerava deklarirani tip objekta i naziv metode, nabraja sve metode s nazivomgovor u klasi AerusTargarien i sve javne metode govor u nadrazredimaAerus Targarien. Prevodilac sada zna moguće kandidate kada poziva metodu.
2. Prevodilac određuje tipove argumenata koji se prosljeđuju metodi. Ako se pronađe samo jedna metoda čiji potpis odgovara argumentima, vrši se poziv.Ovaj procesking.speech("Homo homini lupus est") prevoditelj će odabrati metodugovor (navod niza), ali ne govor().
Ako prevoditelj pronađe više metodas odgovarajućim parametrima (ili bez njih), izdaje se poruka o pogrešci.



Prevodilac sada zna ime i tipove parametara metode koju treba pozvati.
3. U slučaju da je pozvana metodaprivatni, statički, konačniili konstruktor, koristi se statičko vezanje ( rano uvezivanje). U drugim slučajevima, metoda koja se poziva određena je stvarnim tipom objekta kroz koji se poziv pojavljuje. Oni. koristi se tijekom izvođenja programa. dinamičko uvezivanje (kasno uvezivanje).

4. Virtualni stroj unaprijed stvara tablicu metoda za svaku klasu koja navodi potpise svih metoda i stvarnih metoda koje treba pozvati.
Tablica metoda za razredKralj izgleda ovako:
  • govor() - Kralj. govor()
  • govor (navod niza) -Kralj. govor (navod niza)
  • Kralj. govor (Booleov speakLoudly )
I za razredAerysTargaryen - dakle:
  • govor() - AerysTargaryen . govor()
  • govor (navod niza) - AerysTargaryen. govor (navod niza)
  • govor (Booleov speakLoudly) -Kralj. govor (Booleov speakLoudly )
Metode naslijeđene od Object-a zanemarene su u ovom primjeru.
Na pozivkralj.govor() :
  1. Određuje se stvarni tip varijablekralj . U ovom slučaju, ovoAerysTargaryen.
  2. Virtualni stroj određuje klasi kojoj metoda pripadagovor()
  3. Metoda se zove.
Povezivanje svih metoda uJavaprovodi se polimorfno, kasnim vezanjem.Dinamičko uvezivanje ima jednu važnu značajku: dopuštamodificirati programe bez ponovnog kompajliranja njihovih kodova. Ovo čini programedinamički proširivo ( proširivo).
Što se događa ako pozovete dinamički vezanu metodu na objektu koji se konstruira u konstruktoru? Na primjer:
class King ( King() ( System .out .println ("Poziv King konstruktora") ; govor() ; //polimorfna metoda nadjačana u AerysTargaryen) javni nevažeći govor() ( System .out .println ("Ja sam kralj Andala!" ) ; ) ) klasa AerysTargaryen proširuje King (privatni String žrtveName; AerysTargaryen() ( System .out .println ( "Nazovi konstruktora Aerys Targaryen") ; žrtveName = "Lyanna Stark" ; govor() ; ) @Override public void speak() ( System .out .println ("Burn " + žrtvaName + "!" ) ; ) ) class Kingdom ( public static void main(String args) ( King king = new AerysTargaryen() ; ) ) Proizlaziti:

Zovite King konstruktora Burn null! Nazovite Aerys Targaryen konstruktoricu Burn Lyannu Stark!
Konstruktor osnovne klase uvijek se poziva tijekom konstrukcije izvedene klase. Poziv automatski putuje uz lanac nasljeđivanja tako da se na kraju pozivaju konstruktori svih osnovnih klasa duž lanca nasljeđivanja.
To znači da kada se pozove konstruktor novi AerysTargaryen() će se zvati:
  1. novi objekt()
  2. novi kralj()
  3. novi AerysTargaryen()
Po definiciji, posao konstruktora je dati život objektu. Unutar bilo kojeg konstruktora objekt se može samo djelomično formirati - poznato je samo da su objekti osnovne klase inicijalizirani. Ako je konstruktor samo još jedan korak u konstruiranju objekta klase izvedene iz klase danog konstruktora, "izvedeni" dijelovi još nisu inicijalizirani u trenutku poziva trenutnog konstruktora.

Međutim, dinamički vezan poziv može ići u "vanjski" dio hijerarhije, odnosno u izvedene klase. Ako poziva metodu izvedene klase u konstruktoru, to može dovesti do manipulacije neinicijaliziranim podacima, što vidimo kao rezultat ovog primjera.

Rezultat programske operacije određen je izvršavanjem algoritma za inicijalizaciju objekta:

  1. Memorija dodijeljena novom objektu popunjava se binarnim nulama.
  2. Konstruktori osnovne klase pozivaju se redoslijedom koji je ranije opisan. U ovom trenutku se poziva nadjačana metoda govor() (da, prije pozivanja konstruktora klaseAerysTargaryen), gdje je utvrđeno da varijablažrtvaName je nula zbog prvog koraka.
  3. Inicijalizatori članova klase pozivaju se redoslijedom kojim su definirani.
  4. Izvršava se tijelo konstruktora izvedene klase.
Konkretno, zbog takvih problema u ponašanju, vrijedi se pridržavati sljedećeg pravila za pisanje konstruktora:
- izvoditi u konstruktoru samo najnužnije i najjednostavnije akcije za inicijalizaciju objekta
- kad god je to moguće, izbjegavajte pozivanje metoda koje nisu definirane kao privatni ili konačni (Što je isto u ovom kontekstu.)
Korišteni materijali:
  1. Eckel B.- Razmišljanje u Javi , 4. izdanje - 8. poglavlje
  2. Cay S. Horstmann, Gary Cornell - Core Java 1 - Poglavlje 5
  3. Wikipedia

Primjena odraza, kasnog povezivanja i atributa

U ovom članku predlažem pogledati složeni primjer korištenja odraza, kasnog uvezivanja i atributa. Pretpostavimo da je zadatak bio razviti tzv. proširivu aplikaciju na koju bi se mogli spojiti alati treće strane.

Što se točno podrazumijeva pod proširivom primjenom? Razmotrite IDE Visual Studio 2010. Tijekom razvoja, ova je aplikacija dobila posebne "zamke" (hook) kako bi se drugim dobavljačima softvera omogućilo povezivanje njihovih posebnih modula. Jasno je da programeri Visual Studio 2010 nisu mogli dodati reference nepostojećim vanjskim sklopovima .NET-a (tj. koristiti rano vezanje), pa kako su uspjeli osigurati potrebne metode povezivanja u aplikaciji? Jedan od mogućih načina rješavanja ovog problema opisan je u nastavku.

    Prvo, svaka proširiva aplikacija mora imati neku vrstu mehanizma unosa koji korisniku omogućuje navođenje dodatka (na primjer, dijaloški okvir ili odgovarajuću oznaku naredbenog retka). To zahtijeva korištenje dinamičkog opterećenja.

    Drugo, svaka proširiva aplikacija nužno mora moći odrediti podržava li modul funkcionalnost (npr. skup sučelja) potrebnu za povezivanje s okolinom. To zahtijeva korištenje odraza.

    Treće, svaka proširiva aplikacija mora biti sigurna da će dobiti referencu na potrebnu infrastrukturu (npr. skup tipova sučelja) i pozvati svoje članove da pozovu temeljnu funkcionalnost. To može zahtijevati korištenje kasnog povezivanja.

Ako je proširiva aplikacija inicijalno programirana da zahtijeva određena sučelja, ona je u stanju odrediti u vrijeme izvođenja može li se pozvati vrsta interesa i, nakon što vrsta uspješno prođe takvu provjeru, dopustiti joj da podržava dodatna sučelja i pristupa njihovoj funkcionalnosti u polimorfan način. To je upravo pristup koji su zauzeli programeri Visual Studio 2010, iu njemu nema ništa posebno komplicirano.

Prvi korak je stvaranje sklopa s tipovima koje svaki snap-in mora koristiti kako bi se mogao povezati s aplikacijom koja se proširuje. Da biste to učinili, kreirajte projekt tipa Class Library (Knjižnica razreda) i u njemu definirajte sljedeća dva tipa:

Korištenje sustava; namespace PW_CommonType ( javno sučelje IApplicationFunc ( void Go(); ) javna klasa InfoAttribute: System.Attribute ( javni niz CompanyName ( get; set; ) javni niz CompanyUrl ( get; set; ) ) )

Zatim trebate stvoriti tip koji implementira sučelje IApplicationFunc. Kako bi primjer stvaranja proširive aplikacije bio jednostavan, zadržimo ovu vrstu jednostavnom. Kreirajmo novi projekt tipa Class Library u C# i definirajmo tip klase pod nazivom MyCompanyInfo u njemu:

Korištenje sustava; koristeći PW_CommonType; koristeći System.Windows..Go() ( MessageBox.Show("Važne informacije!"); ) ) )

I konačno, posljednji korak je stvaranje najproširivije Windows Forms aplikacije, koja će korisniku omogućiti odabir željenog snap-ina pomoću standardnog Windows dijaloškog okvira za otvaranje datoteke.

Sada mu trebamo dodati referencu na sklop PW_CommonType.dll, ali ne na biblioteku koda CompanyInfo.dll. Također morate uvesti imenske prostore System.Reflection i PW_CommonType u glavnu datoteku koda obrasca (da biste je otvorili, desnom tipkom miša kliknite vizualni dizajner obrasca i odaberite Prikaži kod iz kontekstnog izbornika). Podsjetimo se da je svrha ovog dodatka vidjeti kako koristiti kasno vezanje i refleksiju za testiranje pojedinačnih binarnih datoteka koje su proizveli drugi dobavljači na njihovu sposobnost da djeluju kao dodaci.

Udio: