Kasni uvez. Što je "obvezujući" i zbog čega kasni? - Nevjerojatne avanture u kodu

Kasni uvez S COM komponente

Prije nego što izvršna datoteka klijenta može pozvati metode i svojstva bean objekta, mora znati memorijske adrese tih metoda i svojstava. Postoje dvije različite tehnologije koje klijentski programi mogu koristiti za određivanje tih adresa.

Programi ranog povezivanja uče adrese rano u procesu kompilacije/izvršenja, tijekom kompilacije. Kada se program kompajlira ranim vezanjem, kompajler koristi biblioteku tipova komponente da uključi adrese metoda i svojstava komponente u izvršnu datoteku klijenta tako da se adresama može pristupiti vrlo brzo i bez grešaka. Tehnologije COM interop o kojima se dosad raspravljalo koriste rano vezanje.

Sa svoje strane, kasno povezani programi uče adrese svojstava i metoda kasno u procesu kompilacije/izvršenja, upravo u trenutku kada se ta svojstva i metode pozivaju. Kasno vezani kod obično pristupa klijentskim objektima putem osnovnih tipova podataka kao što je objekt i koristi okruženje vremena izvođenja za dinamičko određivanje adresa metoda. Iako kasno vezani kod dopušta neke složene tehnike programiranja kao što je polimorfizam, dolazi s nekim povezanim dodatnim troškovima, što ćemo uskoro vidjeti.

Ali prvo, provjerimo kako se kasno vezanje izvodi korištenjem refleksije u C# (Refleksija je način na koji kod koristi za vrijeme izvođenja kako bi odredio informacije o sučeljima klasa poslužitelja; pogledajte Poglavlje 5.)

Kada se kasno vežete za COM objekt u C# programu, ne morate kreirati RCW za COM komponentu. Umjesto toga, poziva se metoda klase GetTypeFromProgID klase Type za instanciranje objekta koji predstavlja tip COM objekta. Klasa Type član je imenskog prostora System.Runtime.InteropServices, a u donjem kodu konfiguriramo objekt Type za istu COM komponentu pristupa podacima korištenu u prethodnim primjerima:


Tip objCustomerTableType;

Kada postoji Type objekt koji enkapsulira informacije o tipu COM objekta, on se koristi za stvaranje instance samog COM objekta. To se postiže prosljeđivanjem Type objekta metodi klase CreateInstance Activatora. CreateInstance instancira COM objekt i vraća kasno vezanu referencu na njega, koja se može pohraniti u referencu tipa objekta.

objekt objCustomerTable;
objCustomerTable = Activator.CreateInstance(objCustomerTableType);

Nažalost, nije moguće pozvati metode izravno na referencu tipa object. Za pristup COM objektu morate koristiti metodu InvokeMember objekta Type koji je prvi kreiran. Kada se pozove metoda InvokeMember, prosljeđuje joj se referenca na COM objekt, zajedno s nazivom COM metode koju treba pozvati i nizom tipa objekta svih ulaznih argumenata metode.

ObjCustomerTableType.InvokeMember("Delete", BindingFlags.InvokeMethod, null, objCustomerTable, aryInputArgs);

Ponovno se prisjetite slijeda radnji:

1. Stvorite Type objekt za tip COM objekta pomoću metode klase Type.GetTypeFromProgID().

2. Koristite ovaj Type objekt za stvaranje COM objekta koristeći Activator.CreateInstance() .

3. Metode se pozivaju na COM objektu pozivanjem metode InvokeMember na Type objektu i prosljeđivanjem reference objekta kao ulaznog argumenta. Ispod je primjer koda koji sve ovo kombinira u jedan blok:

koristeći System.Runtime.InteropServices;
Tip objCustomerTableType;
objekt objCustomerTable;
objCustomerTableType=Type.GetTypeFromProgID("DataAccess.CustomerTable");
objCustomerTable=Activator.CreateInstance(ObjCustomerTableType);
objCustomerTableType.InvokeMember("Delete", BindingFlags, InvokeMethod, null, objCustomerTable, aryInputArgs);
objCustomerTableType = Type.GetTypeFromProgID("DataAccess.CustomerTable");

Dok C#-ove značajke kasnog povezivanja izbjegavaju poteškoće RCW-a, postoje neki nedostaci kojih biste trebali biti svjesni.

Prvo, kasno vezanje može biti opasno. Kada se koristi rano vezanje, prevodilac može postaviti upit biblioteci tipova COM komponente kako bi provjerio postoje li sve metode pozvane na COM objekte. Uz kasno vezanje, ne postoji ništa što bi spriječilo tipfeler u pozivu metode InvokeMember(), koja može generirati pogrešku vremena izvođenja.

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. Dakle, č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 pozvana i pozivajuća metoda biti vezane kada se poziv stvarno izvrši.

Očito je da je brzina i učinkovitost ranog vezanja veća od one kod 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, zamjenjuje odgovarajuću metodu posvuda, č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 uzima 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 vozila. U osnovnoj klasi vozila 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 vozila. 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 vozila, 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 vozila je virtualna ( virtualan), tada bi prevodilac statički (hard) vezao bilo koji poziv metode na objekt pokazivača monocikl s metodom poruka razreda vozila, jer pri opisivanju rekli smo da varijabla monocikl pokazuje na objekt klase vozila. 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 vozila.

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 poziva zadane 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.

2

Recimo da nije bilo funkcije hello i da jednostavno pozovemo ob.display u osnovi, a onda ona 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 prikaza izvedene klase bez korištenja virtualne ključne riječi (kasno vezanje) 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? -

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 aplikacija je opremljena posebnim "zamkama" (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 specificiranje dodatka (npr. 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 osigurati dobivanje reference 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; pomoću 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.

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 naziv 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 se automatski prosljeđuje 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 vezani 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
Udio: