Scrigroup - Documente si articole

Username / Parola inexistente      

Home Documente Upload Resurse Alte limbi doc  

 
CATEGORII DOCUMENTE





AccessAdobe photoshopAlgoritmiAutocadBaze de dateCC sharp
CalculatoareCorel drawDot netExcelFox proFrontpageHardware
HtmlInternetJavaLinuxMatlabMs dosPascal
PhpPower pointRetele calculatoareSqlTutorialsWebdesignWindows
WordXml


EXTINDEREA CLASELOR - Cum se definesc clasele extinse

c

+ Font mai mare | - Font mai mic







DOCUMENTE SIMILARE

Trimite pe Messenger
Fisiere
Notiunea de algoritm
Probleme rezolvate in C la informatica
adaugarea unui element in lista si stergerea primului element par
Prezentarea generala a limbajului C++
Metoda Newton (tangentei)
Instructiunea do while
Instructiuni si blocuri
Variabile globale
Operatori si expresii de asignare

EXTINDEREA CLASELOR

Cum se definesc clasele extinse

Limbajul Java pune la dispozitie si o alta facilitate importanta legata de OOP : posibilitatea de extindere a claselor. Pe scurt, deci incomplet, aceasta consta in:

- o clasa poate fi extinsa, adaugandu-se noi campuri si noi metode, care permit considerarea unor atribute suplimentare (daca ne gandim la campuri) si unor operatii noi (asupra campurilor 'initiale', dar si asupra campurilor nou adaugate);

- unele metode ale clasei pe care o extindem pot fi redefinite, iar anumite campuri ale acestei clase pot fi 'ascunse';

- un obiect avand ca tip clasa extinsa poate fi folosit oriunde este asteptat un obiect al clasei care a fost extinsa.

La prima vedere, rezolvarea problemelor de mai sus se poate face simplu: modificam clasa, introducand sau/si modificand campurile si metodele clasei. In practica programarii, aceasta solutie este de neconceput. Pe de o parte se pot introduce erori, iar pe de alta parte utilizatorii clasei vechi nu vor mai putea folosi clasa asa cum o faceau inainte si cum vor in continuare sa o faca; clasa (firma care a elaborat-o) isi va pierde astfel vechii clienti. Sa retinem deci ca o regula generala de programare faptul ca nu trebuie modificata o clasa testata si deja folosita de multi utilizatori; cu alte cuvinte, nu trebuie modificat 'contractul' ce a dus la scrierea clasei.

De aceea vechea clasa trebuie sa ramana nemodificata, iar actualizarea ei trebuie facuta prin mecanismul de extindere a claselor, prezentat in continuare.

Exemplu. Sa presupunem ca dorim sa urmarim miscarea unui punct in plan. Vom incepe prin a considera clasa:

class Punct

void Origine()

Punct Miscare(int x, int y)

}

Clasa Punct contine:

- campurile x, y, urm ;

- constructorul Punct cu signatura (int, int);

- metoda Origine cu signatura () si metoda Miscare cu signatura (int,int).

Vom extinde clasa Punct astfel incat punctul sa aiba si o culoare:

class Pixel extends Punct

void Origine()

}

Clasa Pixel contine:

- campurile x,y,urm (mostenite de la clasa Punct) si culoare (care a fost adaugat prin extindere);

- constructorul Pixel cu signatura (int,int,String);

- metoda Miscare cu signatura (int,int), mostenita de la clasa Punct, precum si metoda Origine cu signatura (), care redefineste metoda Origine a clasei Punct.

Este adoptata urmatoarea terminologie: clasa Punct este superclasa a lui Pixel, iar Pixel este subclasa (clasa extinsa) a lui Punct.

In Java, clasele formeaza o structura de arbore in care radacina este clasa Object, a carei definitie apare in pachetul java.lang. Orice clasa extinde direct (implicit sau explicit) sau indirect clasa Object. Stim ca in informatica arborii 'cresc in jos', deci radacina (clasa Object) se afla pe cel mai de sus nivel.

Termenii de superclasa si subclasa se refera exclusiv la relatia tata fiu, conform relatiei de extindere, in arborele de clase. Este incorect de a interpreta acesti termeni in sensul de incluziune!

In exemplul de mai sus, apelurile super.Origine() si super(x,y) se refera respectiv la metoda Origine si la constructorul din superclasa Punct a lui Pixel. Vom reveni insa cu toate detaliile legate de super.

Fie Sub o clasa ce extinde clasa Super. Sunt importante urmatoarele precizari:

- obiectele de tip Sub pot fi folosite oriunde este asteptat un obiect de tipul Super. De exemplu daca un parametru al unei metode este de tipul Super, putem invoca acea metoda cu un argument de tipul Sub;

- spunem ca Sub extinde comportarea lui Super;

- o clasa poate fi subclasa a unei singure clase (mostenire simpla) deoarece, reamintim, clasele formeaza o structura de arbore. Drept consecinta o clasa nu poate extinde doua clase, deci nu exista mostenire multipla ca in alte limbaje (exista insa mecanisme pentru a simula aceasta facilitate si anume interfetele);

- o metoda din superclasa poate fi rescrisa in subclasa. Spunem ca metoda este redefinita (daca nu este statica) sau ascunsa (daca este statica). Evident, ne referim aici la o rescriere a metodei folosind aceeasi signatura, pentru ca daca scriem in subclasa o metoda cu o signatura diferita de signaturile metodelor cu acelasi nume din superclasa, atunci va fi vorba de o metoda noua. La rescriere nu putem modifica tipul valorii intoarse de metoda. Accesul la metoda cu aceeasi signatura din superclasa se face, dupa cum vom vedea, prin precalificare cu super;

- un camp redeclarat in Sub ascunde campul cu acelasi nume din Super;

- campurile neascunse si metodele nerescrise sunt automat mostenite de subclasa (in exemplul de mai sus este vorba de campurile x si y si de metoda Miscare);

- apelurile super din constructorii subclasei trebuie sa apara ca prima actiune.

Despre initializare si constructori

Suntem acum in masura sa prezentam mai complet si actiunile intreprinse la crearea unui obiect.

Prima actiune consta in a atribui campurilor valorile initiale implicite:

0 - pentru tipuri numerice

u0000 - pentru tipul char

false - pentru tipul boolean

null - pentru referinte (la obiecte, inclusiv tablouri si String).

In continuare este invocat constructorul corespunzator (determinat de signatura, in acelasi mod ca pentru metode); actiunea sa cuprinde trei faze:

1) Invocarea unui constructor al superclasei si anume:

- explicit, cu super() ;

- implicit: este vorba de constructorul fara argumente.

2) Executarea, in ordinea in care apar, a initializarilor directe ale campurilor;

3) Executarea corpului constructorului.

Observatii:

- un constructor poate invoca un alt constructor si anume prin this(); aceasta invocare poate avea loc numai ca prima actiune a constructorului (deci nu pot fi invocati doi constructori). In schimb acest mecanism nu poate fi folosit pentru a invoca, din corpul unei metode, un constructor;

- un constructor poate invoca o metoda, caz in care se considera implementarea metodei corespunzatoare tipului real al obiectului (vezi subcapitolul urmator).

Exemplu. Sa consideram clasele:

class a

}

class b extends a

}

La crearea unui obiect de tipul b, prin new b(), au loc in ordine urmatoarele actiuni:

- campurile v, va, vb primesc valoarea implicita 0;

- este invocat constructorul b;

- este invocat constructorul a (al superclasei);

- are loc initializarea directa a lui va, care primeste valoarea 1;

- este executat corpul constructorului a si ca urmare v primeste valoarea 1;

- are loc initializarea directa a lui vb, care primeste valoarea 2;

- este executat corpul constructorului b si ca urmare v primeste valoarea 3.

Daca o clasa este declarata cu modificatorul public, constructorul implicit are automat acest modificator.

Constructorii unei clase nu sunt considerati membri ai clasei. De aceea ei nu pot fi mosteniti sau redeclarati.

Constructorilor li se pot atasa modificatori de acces la fel ca metodelor (vezi subcapitolele urmatoare). Dar, spre deosebire de metode, un constructor nu poate fi declarat cu alti modificatori decat cei de acces.

Intr-un constructor poate fi folosita instructiunea return, dar neinsotita de o expresie.

Daca un constructor se invoca (direct sau indirect) pe el insusi, la compilare este semnalata o eroare.

Intr-o invocare explicita a unui constructor, argumentele nu pot fi instantieri de campuri declarate in aceeasi clasa sau intr-o superclasa; de asemenea nu este permisa utilizarea lui this si super in expresii.

Exemplificam in continuare acest aspect pentru campuri.

Exemplu. Sa consideram urmatoarea clasa:

class C

C(int x)

}

In clasa C, constructorul fara parametri invoca pe cel cu un parametru. Va fi semnalata o eroare la compilare. In schimb clasa va fi corecta daca de exemplu campul x ar fi fost declarat cu modificatorul static.

Rescrierea metodelor si ascunderea campurilor

Campurile pot fi ascunse prin redeclararea lor intr-o subclasa. Campul cu acelasi nume din superclasa nu mai poate fi accesat direct prin numele sau, dar ne putem referi la el folosind super (vezi subcapitolul urmator) sau o referinta la tipul superclasei.



O metoda declarata intr-o clasa poate fi rescrisa intr-o subclasa prin declararea ei in subclasa cu acelasi nume, aceeasi signatura si acelasi tip al valorii intoarse.

In metoda rescrisa putem schimba modificatorul de acces, cu conditia ca dreptul de acces sa creasca; reamintim ca modificatorii de acces, in ordine de la cel mai restrictiv la cel mai permisiv, sunt: private, protected, cel implicit (package) si public.

Spunem ca metodele rescrise sunt ascunse (daca e vorba de metode statice) sau redefinite (in cazul metodelor nestatice). Nu este vorba numai de o diferenta de terminologie, deoarece metodele statice pot fi rescrise (ascunse) numai de metode statice, iar metodele nestatice pot fi rescrise (redefinite) numai de metode nestatice. Alte precizari vor fi facute in continuare.

Fie A o clasa si fie B o subclasa a sa. Sa consideram urmatoarele patru actiuni echivalente din punctul de vedere al obiectului a ce ia nastere:

1) A a; a = new B();

2) A a = new B();

3) A a; B b; b = new B(); a = b;

4) A a; B b; b = new B(); a = (A) b;

Sa observam ca este vorba de o conversie de la o subclasa la o superclasa, conversie numita upcasting ; ea poate fi implicita (ca in primele trei actiuni) sau explicita (ca in cea de a patra actiune).

Vom spune ca obiectul a are tipul declarat A si tipul real B, ceea ce pune in evidenta notiunea de polimorfism.

Tipul real al unui obiect coincide cu tipul sau declarat (este cazul obiectului b) sau este o subclasa a tipului declarat (vezi obiectul a).

Fie camp un camp al clasei A, ce este redeclarat (ascuns) in subclasa B. Daca obiectul a face referire la campul camp, atunci este vorba de campul declarat in clasa A, adica este folosit tipul declarat al obiectului.

Fie met o metoda a clasei A, care este rescrisa in subclasa B. La invocarea metodei met de catre obiectul a, este folosita fie implementarea corespunzatoare metodei ascunse (daca este statica), fie cea corespunzatoare metodei redefinite (daca este nestatica). Cu alte cuvinte, pentru metode statice este folosit tipul declarat (la fel ca pentru campuri), iar pentru metode nestatice este folosit tipul real al obiectului. In ambele cazuri, se pleaca de la tipul indicat mai sus si se merge in sus spre radacina (clasa Object) in arborele de clase pana cand se ajunge la primul tip in care apare metoda respectiva (evident cu signatura corespunzatoare); inexistenta unui astfel de tip este semnalata chiar in faza de compilare.

Exemplu. Urmatorul program:

class Super

void met2()

}

class Sub extends Super

void met2()

}

class Test1

}

produce la iesire:

static_Super

Sub

Exemplu. Sa consideram urmatorul program:

class A

}

class B extends A

}

class AB1

La executare sunt tiparite urmatoarele:

B : Sub

B : Sub

Super Sub

adica rezultatele asteptate, tinand cont ca:

- pentru obiectul b: tipul declarat coincide cu cel real si este B;

- pentru obiectul a: tipul declarat este A, iar tipul real este B.

Sa consideram o clasa C si doua subclase X si Y ale sale, precum si urmatoarea secventa de instructiuni:

C Ob;

. . . Ob = new X();

. . . Ob = new C();

. . . Ob = new Y();

. . .

in care Ob are mai intai tipul real X, apoi tipul real C, apoi tipul real Y. Spunem ca Ob este o variabila polimorfica, deoarece are pe rand forma (comportamentul) a mai multor clase.

Cuvantul cheie super

Putem folosi cuvantul cheie super in orice metoda nestatica a unei clase extinse, si anume sub una dintre formele:

- super() : pentru a invoca un constructor al superclasei clasei curente;

- super.met() : pentru a invoca o metoda a superclasei, metoda ce a fost redefinita in clasa curenta;

- super.c : pentru a accesa un camp c al superclasei, camp ce a fost ascuns in clasa curenta prin redefinirea sa.

Observam ca la accesarea unui camp sau a unei metode, super actioneaza ca referinta la obiectul curent ca instantiere a superclasei sale.

Exemplu. Programul urmator:

class A

void metoda()

}

class B extends A

void metoda()

}

class AB2

}

produce la iesire urmatorul rezultat:

B : Sub Super

A : Super

A:

Sub

****************

B : Sub Super

A : Super

A:

Super

****************

A:

Super

Mostenire, polimorfism si legare dinamica

Reamintim urmatoarele:

- orice obiect care instantiaza o clasa poate fi folosit in orice cod menit sa lucreze cu instantieri ale superclaselor sale;

- orice obiect care instantiaza o clasa poate folosi (in modurile descrise mai sus) codul supraclasei.

La aceste doua caracteristici legate de mostenire (extinderea claselor), Java adauga polimorfismul si legarea dinamica, intre care exista o stransa relatie.

Polimorfismul consta in posibilitatea ca o invocare Ob.met() sa aiba drept consecinta invocarea unei anumite metode cu numele met, in functie de tipul real al obiectului Ob. Unii autori considera ca si cele doua facilitati enuntate mai sus fac parte din conceptul de polimorfism.

Legarea dinamica identifica metoda met respectiva la executarea programului. Polimorfismul si legarea dinamica sunt doua caracteristici esentiale ale programarii orientate pe obiecte si vor fi analizate in acest subcapitol.

Asa cum stim, fiecare obiect are un tip declarat si un tip real; tipul real coincide cu cel declarat sau este un subtip al acestuia. La invocarea metodelor nestatice se foloseste tipul real, iar la invocarea metodelor statice si la referirea campurilor se foloseste tipul declarat.

Pentru metode nestatice se foloseste tipul real al obiectelor care le invoca deoarece:

- cronologic, superclasa a fost scrisa inaintea clasei si este de presupus ca a fost folosita de mai multi utilizatori, care vor in continuare sa lucreze cu ea;

- in metodele noii clase se pot face referiri la campuri nou definite, ceea ce nu este posibil pentru metodele superclasei.

Utilizarea tipului declarat pentru metodele statice se datoreaza urmatorului fapt: compilatorul 'face tot ce poate' pentru a detecta metoda ce trebuie invocata.

Java foloseste legarea dinamica, adica identificarea metodei nestatice concrete care este folosita la o invocare se face la executare si nu la compilare. De aceea legarea dinamica se mai numeste legare tarzie.

Necesitatea legarii dinamice apare din urmatorul exemplu:

Exemplu. Fie A o clasa in care este definita o metoda met, iar B o subclasa a sa in care metoda met este redefinita. Secventa de instructiuni:

double d = IO.read(); A Ob;

if (d>0) Ob = new A();

else Ob = new B();

Ob.met();

arata ca doar la momentul invocarii metodei devine clar care dintre cele doua metode met va fi invocata.

Despre modificatori

Reluam discutia despre modificatori, pentru a include si aspectele legate de extinderea claselor.

Lista completa a modificatorilor folositi in Java este urmatoarea:

- modificatorii de acces (public, protected, private si cel implicit);

- abstract, static, final, synchronized, native, transient, volatile.

Ei pot fi folositi astfel:

- pentru clase: public,cel implicit, abstract, final;

- pentru interfete: modificatorii de acces;

- pentru constructori: modificatorii de acces;

- pentru campuri: modificatorii de acces, final, static, transient, volatile;

- pentru metode: modificatorii de acces, final, static, abstract, synchronized, native.

Modificatorul final poate fi asociat (in afara variabilelor locale si campurilor) si metodelor si claselor. El trebuie inteles in sensul de 'varianta finala':

- o metoda cu acest modificator nu poate fi rescrisa intr-o subclasa;

- o clasa cu acest modificator nu poate fi extinsa.

Metodele declarate cu private sau/si static sunt echivalente cu final, din punctul de vedere al redefinirii: nu pot fi redefinite.

Daca o metoda este finala, 'ne putem baza' pe implementarea ei (bineinteles daca nu invoca metode nefinale). In unele situatii, este bine sa marcam toate metodele cu final, dar nu si clasa.

DISTRIBUIE DOCUMENTUL

Comentarii


Vizualizari: 462
Importanta: rank

Comenteaza documentul:

Te rugam sa te autentifici sau sa iti faci cont pentru a putea comenta

Creaza cont nou

Distribuie URL

Adauga cod HTML in site



Termeni si conditii de utilizare | Contact
© SCRIGROUP 2019. All rights reserved