0% au considerat acest document util (0 voturi)
100 vizualizări

Curs07 Java

Documentul prezintă informații despre conversia tipurilor de date în Java, pachete de clase și interfete. Sunt explicate conversia implicită și explicită de tipuri, conversia dintre tipuri primitive și obiecte, precum și utilizarea pachetelor existente și crearea propriilor pachete.

Încărcat de

dani10gh
Drepturi de autor
© Attribution Non-Commercial (BY-NC)
Formate disponibile
Descărcați ca PPT, PDF, TXT sau citiți online pe Scribd
0% au considerat acest document util (0 voturi)
100 vizualizări

Curs07 Java

Documentul prezintă informații despre conversia tipurilor de date în Java, pachete de clase și interfete. Sunt explicate conversia implicită și explicită de tipuri, conversia dintre tipuri primitive și obiecte, precum și utilizarea pachetelor existente și crearea propriilor pachete.

Încărcat de

dani10gh
Drepturi de autor
© Attribution Non-Commercial (BY-NC)
Formate disponibile
Descărcați ca PPT, PDF, TXT sau citiți online pe Scribd
Sunteți pe pagina 1/ 39

Cuvinte importante:

- conversia explicita de tip (casting) pentru obiecte: conversie implicita si explicita de tip; - conversia tipurilor primitive in obiecte si invers;

- pachete de clase de obiecte: utilizarea pachetelor existente de clase de obiecte; comanda import; variabila CLASSPATH; locul de dispunere a claselor in sistemul de fisiere; crearea propriilor pachete de clase de obiecte; pachetele de clase si controlul accesului la clase; - mostenire multipla;
- interfata Java: crearea interfetelor; folosirea interfetelor; implementarea unei interfete; implementarea unor interfete multiple; derivarea interfetelor;

- implementarea de componente generice: programarea generica; clase generice.


3/2/2014 1

Conversia explicita de tip (casting) pentru obiecte


Operatia de conversie a instantelor unor clase catre instante ale altor clase se poate face numai daca clasele sursa si destinatie sunt inrudite prin mostenire, adica o clasa trebuie sa fie o subclasa a alteia.
Pentru ca mostenirea permite tratarea unui obiect ca fiind de tip propriu sau de tipul superclasei, unele obiecte nu au nevoie de conversie explicita. Astfel, deoarece subclasele contin toate informatiile superclasei lor, si chiar mai mult, se poate folosi o instanta a unei subclase acolo unde se doreste referirea la o instanta a superclasei. La operatia de atribuire, conversia implicita inseamna ca unei variabile referinta de tipul unei superclase i se poate atribui o referinta la o instanta de tipul oricarei subclase a ei. Variabila de tipul superclasei, folosita in acest fel, se spune ca este polimorfica. Urmatorul exemplu arata o conversie implicita prin atribuirea valorii unei variabile referinta de tipul unei subclase (clasa Pisica) la o variabila de tipul unei superclase (clasa AnimalDeCasa): AnimalDeCasa a = new Pisica(); Variabila referinta a care este de tipul AnimalDeCasa desemneaza un obiect de tipul Pisica.
3/2/2014 2

Conversia explicita intre tipurile obiectelor (sau casting-ul) este necesara atunci cand se foloseste o instanta a superclasei acolo unde se doreste referirea la o instanta a subclasei. Sintaxa folosita pentru conversia explicita de tip este:

(<nume_subclasa>) <nume_variabila_superclasa>
unde: - <nume_subclasa> - numele subclasei catre care se doreste conversia; - <nume_variabila_superclasa> - numele variabilei referinta care indica o instanta a unei superclase din care face parte subclasa cu numele <nume_subclasa>. Nota: Sa retinem ca operatia de casting creaza o referinta de tip <nume_subclasa> la instanta superclasei, in timp ce instanta cu tipul superclasei continua sa existe ca si inainte.

3/2/2014

De exemplu, daca avem o operatie care apeleaza metode ale obiectelor apartinand clasei Integer, folosirea unui obiect din clasa Object nu va dispune de multe din metodele definite in Integer si drept urmare va fi necesara o conversie explicita la tipul Integer, ca mai jos: int elemVarf = ((Integer) s.top()).intValue(); In acest exemplu metoda s.top() returneaza un obiect de tip Object. Pentru a extrage valoarea retinuta in acest obiect trebuie sa-l convertim explicit la tipul Integer, astfel incat sa dispunem de metoda intValue() care returneaza valoarea obiectului convertit la Integer ca o valoare intreaga de tip int.

Conversia tipurilor primitive in obiecte si invers


Conversia de la un tip de data primitiv la un tip referinta si invers nu este posibila in Java in mod implicit. Tipurile primitive si obiectele sunt elemente foarte diferite in Java si nu se poate face conversie automata intre cele doua. In acest sens se poate analiza si instructiunea prezentata mai sus.
Ca alternativa, pachetul Java.lang contine clase de obiecte care corespund fiecarui tip de date primitive: Byte, Short, Integer, Long, Float, Double, Character, Boolean. Totusi, ele sunt interpretate diferit fata de tipurile primitive corespondente si de aceea ele nu pot fi folosite in locul tipurilor primitive si nici invers.
3/2/2014 4

Pentru a crea un obiect de un tip ca: Byte, Short, Integer, Long, Float, Double, Character, Boolean, se foloseste operatorul new. Urmatoarea instructiune creaza o instanta a clasei Integer cu valoarea 44: Integer numarInt = new Integer(44); Obiectul numarInt se poate folosi ca orice alt obiect, beneficiind, in acelasi timp si de toate metodele clasei Integer din biblioteca java.lang. La fel se procedeaza si pentru celelalte clase de obiecte care corespund tipurilor primitive. Pentru a folosi valoarea dintr-un obiect de un tip: Byte, Short, Integer, Long, Float, Double, Character, Boolean, ca valoare de tip primitiv corespunzator, se pot apela metode care realizeaza acest lucru. Metodele des folosite pentru transformarea valorii unui obiect de acest gen in valori de un tip primitiv sunt: byteValue() - pentru valori de tip byte; shortValue() - pentru valori de tip short; intValue() - pentru valori de tip int;
3/2/2014 5

longValue() - pentru valori de tip long;


floatValue() - pentru valori de tip float; doubleValue() - pentru valori de tip double; charValue() - pentru valori de tip char; booleanValue() - pentru valori booleene; De exemplu, pentru a extrage o valoare int din obiectul numarInt, se foloseste urmatoarea instructiune: int numar = numarInt.intValue(); // returneaza valoarea 44.

Pachete de clase de obiecte


Un pachet (package) contine un numar de clase inrudite ca scop, ca domeniu sau din punct de vedere al mostenirii. Daca programele sunt mici si folosesc un numar limitat de clase, nu este necesar sa cream pachete. Insa daca aplicatia noastra foloseste din ce in ce mai multe clase atunci este necesar sa le organizam in pachete.
6

3/2/2014

De ce sunt folositoare pachetele? - Pachetele permit organizarea claselor in grupuri. Asa cum pe hard-disc avem directoare si subdirectoare pentru a ne organiza fisierele si aplicatiile, pachetele ne permit sa organizam clasele in grupuri din care putem folosi doar ceea ce avem nevoie pentru fiecare aplicatie. - Pachetele reduc problemele datorate conflictelor de nume. Cu cat creste numarul claselor de obiecte, cu atat creste posibilitatea de a folosi un nume de clasa deja existent, ceea ce va duce la aparitia unor conflicte si erori la integrarea acestora in aplicatii. Pacetele permit ascunderea claselor si evitarea acestor conflicte. - Pachetele protejeaza clasele, variabilele si metodele depasind nivelul de clasa. - Pachetele pot fi folosite la identificarea claselor. De exemplu, daca implementam un set de clase pentru a realiza o anumita sarcina, putem sa folosim pentru pachetul de clase respectiv un identificator unic, care sa desemneze autorul sau organizatia din care provine.

Nota: Chiar daca un pachet este, in esenta, un grup de clase, acesta poate contine si alte pachete, care formeaza alt nivel ierarhic asemanator, cumva, ierarhiei de clase. Fiecare nivel reprezinta, de obicei, o grupare mai mica de clase si cu sarcini mai precise. Biblioteca de clase Java este organizata si ea dupa aceste principii.
3/2/2014 7

Utilizarea pachetelor existente de clase de obiecte


Stim ca pachetele grupeaza mai multe clase la un loc. Pana acum am folosit pachete din biblioteca Java in legatura cu operatiile de I/O necesare citirii de la tastatura a datelor, pentru generarea unor obiecte de tip Random etc. Ca regula, putem alege unul dintre urmatoarele trei mecanisme pentru a utiliza o clasa continuta intr-un pachet: - cand clasa pe care dorim sa o folosim se afla in pachetul java.lang, ea se poate referi pur si simplu prin numele ei; - cand clasa pe care dorim sa o folosim se afla in alt pachet, ea se poate referi folosindu-i numele complet, adica inclusiv pe cel al pachetului (java.util.Random); - pentru clasele din alte pachete folosite frecvent in aplicatie, se pot importa clasele individuale sau intreg pachetul de clase folosit; dupa ce clasa sau pachetul au fost importate, se poate referi clasa doar prin numele sau.

Nota: Clasele care nu sunt declarate ca facand parte dintr-un anumit pachet sunt automat incluse intr-un pachet prestabilit. Aceste clase pot fi referite prin numele lor, de oriunde din cod.
3/2/2014 8

Comanda import Pentru a importa clasele dintr-un pachet se foloseste comanda import, asa cum am vazut in multe exemple prezentate pana acum. Se poate importa o clasa ca in exemplul urmator: import java.util.Vector;

Se poate importa un intreg pachet de clase, folosind simbolul asterisc (*) in locul numelor de clasa, astfel:
import java.io.*; Nota: Comanda din exemplul de mai sus nu importa toate clasele pachetului; sunt importate doar acele clase care au fost declarate public, si chiar si asa sunt importate doar acele clase care sunt referite in cod. De asemenea, daca pachetul respectiv are si subpachete, prin comanda import din exemplul de mai sus, nu se vor importa si subpachetele pachetului specificat in comanda import. Pentru a importa toate clasele dintr-o ierarhie complexa de pachete va trebui sa se importe explicit fiecare nivel al ierarhiei. Instructiunea import trebuie plasata inaintea oricarei definitii de clasa (insa dupa definitia pachetului, daca se doreste crearea unui pachet propriu, asa cu se va prezenta in sectiunea urmatoare).
3/2/2014 9

Spre deosebire de directiva #include din limbajul C/C++, care include cod-sursa dintr-un alt fisier, instructiunea import indica doar locul unde poate fi gasita o clasa si nu are nici un rol in marirea dimensiunii aplicatiei proiectate. Observatie: Trebuie evitate conflictele de nume de clasa, deoarece compilatorul va semnala o eroare. Conflicul de nume de clasa are loc atunci cand doua clase din pachete diferite au acelasi nume. In aceasta situatie, referirea la clasa dorita din aceste pachete trebuie sa se faca prin numele sau complet (inclusiv numele pachetului). Variabila CLASSPATH si locul de dispunere a claselor in sistemul de fisiere

Pentru ca programul nostru sa poata folosi o clasa, trebuie sa afle locul unde este dispusa clasa in sistemul de fisiere; altfel, va fi generata o eroare referitoare la inexistenta clasei. Pentru gasirea claselor Java foloseste:
- numele pachetului;

- directoarele referite de variabile de mediu CLASSPATH.


Numele pachetelor corespund unor nume de directoare ale sistemului de fisiere.

3/2/2014

10

De exemplu, clasa java.applet.Applet se va gasi in directorul applet care face parte si el din directorul java (adica, java\applet\Applet.class).

Java cauta aceste directoare, pe rand, in cadrul cailor (path-urilor) incluse in variabila de mediu CLASSPATH, daca aceasta este definita. Daca nu este configurata nici o variabila CLASSPATH, Java cauta in directorul prestabilit, java\lib (aflat in directorul cu versiunea de soft SDK folosita), precum si in directorul curent.
Java cauta, in aceste directoare, clasa referita in fisierul-sursa folosindu-se de numele pachetului si al clasei, iar daca nu o gaseste semnaleaza eroare de tip class not found.

3/2/2014

11

Crearea propriilor pachete de clase de obiecte Pentru crearea unor pachete proprii de clase de obiecte se parcurg urmatoarele etape:

1. Alegerea unui nume pentru pachet.


Numele ales pentru pachet depinde de modul in care dorin sa folosim aceste clase. Conventia de denumire a pachetelor, recomandata de Sun este de a folosi numele de domeniu Internet cu elementele inversate. Ideea este de a ne asigura ca numele pachetului creat este unic. Prin conventie, numele pachetelor incep cu litera mica, pentru a le deosebi de numele claselor, care incep cu litera mare. De exemplu, in cazul denumirii complete a clasei String (adica, java.lang.String) putem vizualiza foarte usor numele pachetului de numele clasei. Aceasta conventie ajuta si ea la reducerea conflictelor de nume.

3/2/2014

12

2. Crearea structurii de directoare. Se creaza pe hard-disc o structura de directoare conforma cu numele pachetelor. Daca pachetul are un singur nume (cum ar fi: pachetulmeu) este suficient sa cream un director cu acest nume. Daca numele pachetului este compus din mai multe parti (cum ar fi: ro.utcb.pachetulmeu), trebuie create si subdirectoarele respective. De exemplu, pentru pachetul ro.utcb.pachetulmeu trebuie sa cream directorul ro, in cadrul acestuia subdirectorul utcb, iar in cel din urma, subdirectorul pachetulmeu. Clasele si fisierele sursa vor fi pastrate apoi in directorul pachetulmeu. 3. Adaugarea unei clase intr-un pachet. Pasul final este de a introduce clasa intr-un pachet, folosind instructiunea package, plasata inainte de orice instructiune import folosita. Se poate adauga o clasa la un pachet ca in exemplul urmator: package ro.utcb.pachetulmeu;

Nota: Folosirea unui mediu de dezvoltare de aplicatii de tipul IDE NetBeans usureaza crearea pachetelor si a structurii de directoare aferente acestora. Mediile de acest tip permit si crearea de proiecte care inglobeaza pachetele si alte fisiere necesare aplicatiei, creand de fapt aplicatii de tip .jar.
3/2/2014 13

Pachetele de clase si controlul accesului la clase Controlul accesului la clase se poate realiza folosind modificatorul de acces public. Acest acces inseamna ca respectiva clasa este vizibila si poate fi importata in afara pachetului. Clasele declarate publice pot fi importate de orice alte clase din afara pachetului. Atunci cand folosim instructiunea import cu simbolul * , se vor importa numai clasele publice din pachetul respectiv. Daca nu este specificat nici un modificator pentru clasa, atunci acesteia i se atribuie un control prestabilit al accesului de tip package-friendly. Acest acces presupune ca respectiva clasa este disponibila (vizibila) tuturor claselor din acelasi pachet, insa nu si in afara pachetului- nici macar subpachetelor.Clasa nu poate fi importata sau referita prin nume.

3/2/2014

14

Exemplu de pachete proprii de clase create pentrun aplicatia TestFormaInterfata.java Ne propunem sa rezolvam urmatoarea problema: Se citesc N forme geometrice (patrate, dreptunghiuri, cercuri) de la tastatura. Sa se afiseze formele geometrice ordonate dupa arie.

Pentru rezolvarea acestei probleme s-au definit urmatoarele clase de obiecte:


- clasa abstracta a formelor geometrice cu numele FormaGeo; - clasele derivate din superclasa FormaGeo care caracterizeaza formele geometrice specifice: Cerc, Dreptunghi si Patrat;

- doua clase ajutatoare, care pot fi folosite si de alte aplicatii si anume:


- interfata Compra (despre folosirea interfetei vom vorbi ceva mai tarziu in aceasta lectie) care declara o metoda publica cu numele comparaCu(), asemanatoare metodei din biblioteca Java compareTo(), folosita pentru compararea ariei a doua obiecte de tip FormaGeo; - clasa Sort care realizeaza ordonarea formelor geometrice concrete (Cerc, Patrat si Dreptunghi) dupa arie; acesta clasa are un caracter mai general si poate fi folosita pentru ordonarea altor obiecte dupa diferite criterii.
3/2/2014 15

- clasa aplicatiei propriu-zise cu numele TestFormaInterfata folosita pentru citirea formelor de la tastatura si afisarea formelor geometrice ordonate dupa arie. Pentru o mai buna organizare, clasele de obiecte enumerate au fost impartite pe pachete, ceea ce inseamna ca fiecare fisier de tip .class a fost stocat intr-un director corespunzator numelui pachetului. Astfel, au fost create urmatoarele pachete de clase de obiecte: - pachetul cu numele clasegenerice care contine fisierele de tip .class: Sort.class si Compara.class; acest pachet a fost stocat in directorul cu numele clasegenerice;

- pachetul cu numele formegeometrice care contine fisierele de tip .class: Cerc.class, Dreptunghi.class, Patrat.class si FormaGeo.class; acest pachet a fost stocat in directorul cu numele formegeometrice.
Fisierul TestFormaInterfata.class corespunzator aplicatiei propriu-zise nu a fost inclus in nici un pachet si a fost stocat in directorul curent de lucru aplicatiipachete. Pentru ca aplicatia sa functioneze, acest director trebuie adaugat la variabila de mediu CLASSPATH. In directorul curent s-au creat si directoarele corespunzatoare pachetelor descrise.
3/2/2014 16

In concluzie, structura de directoare si pachetele continute in aceste directoare arata ca in schema de mai jos:
Directorul de lucru aplicatiipachete

Fisierul aplicatiei TestFormaInterfata.class

Directorul clasegenerice

Directorul formegeometrice

Fisierele Sort.class si Compara.class

Fisierele Cerc.class, Dreptunghi.class, Patrat.class si FormaGeo.class

Descrierea detaliata a programelor-sursa se va face in capitolul referitor la interfete.


3/2/2014 17

Mostenire multipla. Interfata


In cazul mostenirii multiple o clasa este derivata din doua sau mai multe superclase. De exemplu, pot exista in ierarhie clase precum Student si Angajat. Din aceste doua clase ar putea fi derivata o clasa cu numele AngajatStudent care sa contina atribute si metode combinate din clasele Student si Angajat.
Mostenirea multipla poate conduce insa la dificultati de proiectare. De exemplu, cele doua superclase din care deriveaza subclasa ar putea contine metode care au aceeasi semnatura, dar implementari diferite sau ar putea avea atribute cu acelasi nume. Dificultatea rezolvarii unor astfel de probleme a facut ca Java sa nu permita mostenirea multipla. Alternativa oferita de Java pentru mostenirea multipla este folosirea interfetelor. O interfata Java este o colectie de comportamente abstracte, care pot fi combinate in orice clasa pentru a introduce in acea clasa comportamente care nu pot fi mostenite de la superclasa. Tehnic, interfata Java este cea mai abstracta clasa posibila. Ea consta doar din metode publice abstracte si din atribute statice si finale. O clasa implementeaza o anumita interfata daca furnizeaza definitii pentru toate metodele abstracte din cadrul interfetei. O clasa care implementeaza o interfata se comporta ca si cand ar fi extins o clasa abstracta specificata de acea interfata.
3/2/2014 18

Crearea si folosirea interfetelor Java

Sintaxa de definire a unei interfete este urmatoarea:


[<modificatori] interface <nume_interfata> [extends <nume_interfata1> [, <nume_interfata2>][, <nume_interfata3>], ]] {

<corpul interfetei>
} unde: - <modificatori> - sunt specificati prin cuvintele-cheie public si abstract; o interfata publica poate fi accesata si de alte pachete decat cel care a definit-o; fiecare interfata este in mod implicit abstracta deci cuvantul-cheie abstract poate lipsi; - <nume_interfata> - specifica numele interfetei; este de preferat ca numele interfetei sa respecte aceeasi conventie de numire ca si cea de numire a claselor; - <nume_interfata1>, <nume_interfata2>, .. - specifica numele superinterfetelor din care poate deriva interfata; - <corpul_clasei> - contine declaratii de variabile si declaratii de metode (numai antetul acestora).
3/2/2014 19

Variabilele interfetei sunt implicit statice si finale si deci trebuie specificate valori initiale pentru acestea.
Metodele sunt declarate in interfata numai cu antetul lor, fara corp. Ele sunt implicit abstracte. Observatii: 1. Interfetele nu se pot instantia, adica nu se poate crea o instanta a unei interfete; deci, o interfata nu are o metoda constructor. 2. Nu pot exista implementari diferite pentru aceeasi metoda declarata in doua interfete. Interfetele trebuie sa posede o protectie de pachet sau publica. Interfetele care nu contin modificatorul public nu-si vor converti automat metodele la accesul public si abstract si nici constantele la accesul public. O interfata nepublica are metode si constante nepublice, acestea neputand fi folosite decat in clasele sau interfetele din acelasi pachet. Interfetele, ca si clasele, pot apartine unui pachet daca se foloseste instructiunea package in prima linie din fisierul-sursa. De asemenea, interfetele pot importa interfete sau clase din alte pachete.
3/2/2014 20

Un exemplu de interfata este Compara, prezentata mai jos:

/* INTERFATA folosita de mai multe clase pentru comparatii * --------------------Metode publice--------------* int comparaCu --> Compara doua obiecte de tip Compara * metoda trebuie definita in clasa care o implementeaza */ package clasegenerice;

public interface Compara


{ int comparaCu(Compara rhs); }

3/2/2014

21

Interfata Compara declara o metoda pe care orice clasa derivata din ea (in declaratia clasei se foloseste cuvantul-cheie implements) trebuie sa o implementeze. Metoda comparaCu () se va comporta similar cu metoda compareTo() din clasa String. Metoda este implicit publica si abstracta si deci nu trebuie sa folosim modificatorii de acces public si de metoda abstract. Implementarea unei interfete O clasa implementeaza o interfata in doi pasi: - declara ca implementeaza interfata, folosind cuvantul-cheie implements in antetul sau; - defineste implementari pentru toate metodele din interfata. Pentru a exemplifica implementarea unei interfete ne vom referi la clasa FormaGeo descrisa in lectia 6. Aceasta clasa implementeaza interfata Compara si, deci, trebuie sa definim si metoda comparaCu() din interfata Compara. Implementarea metodei in clasa FormaGeo trebuie sa fie identica cu declaratia din interfata si, din acest motiv, metoda comparaCu() are ca parametru un obiect de tip Compara si nu un obiect de tip FormaGeo. Codul clasei este ilustrat in continuare (FormaGeo.java):
3/2/2014 22

/* Superclasa abstracta pentru forme */ package formegeometrice; import clasegenerice.*; public abstract class FormaGeo implements Compara { private String nume; abstract public double arie(); public FormaGeo(String numeForma) { nume = numeForma; } public int comparaCu(Compara rhs) { if (arie() < ((FormaGeo) rhs).arie()) return -1; else if (arie() == ((FormaGeo) rhs).arie()) return 0; else return 1; } final public String toString() { return nume + ", avand aria " + arie(); } }
3/2/2014 23

Nota: 1. O clasa care implementeaza o interfata poate sa extinda si o alta clasa. In acest caz in declaratia clasei se foloseste mai intai cuvantul-cheie extends urmat de numele clasei din care deriveaza si apoi cuvantul-cheie implements urmat de numele interfetei pe care o implementeaza. 2. Retinem ca definirea metodelor declarate intr-o interfata trebuie obligatoriu sa fie facuta in fiecare clasa in parte, care implementeaza interfata respectiva. 3. Metodele interfetei definite in clasa care o implementeaza sunt mostenite de toate subclasele clasei respective. Aceste metode pot fi redefinite (suprascrise) in subclase. 4. Daca o clasa extinde o superclasa care implementeaza o interfata, atunci si clasa respectiva mosteneste interfata.

3/2/2014

24

Implementarea unor interfete multiple

O clasa poate sa implementeze mai mult decat o singura interfata. O clasa poate implementa mai multe interfe in doi pasi:
- declara toate interfetele pe care le implementeaza, folosind cuvantul-cheie implements in antetul sau urmat de numele tuturor interfetelor separate prin virgula;

- defineste implementari pentru toate metodele din toate interfetele pe care le implementeaza.
Folosirea unor interfete multiple poate crea totusi complicatii. Derivarea interfetelor

Ca si in cazul claselor, interfetele pot fi organizate intr-o ierarhie. Atunci cand o interfata mosteneste o alta interfata, subinterfata primeste toate metodele si constantele definite in superinterfata.
Pentru a deriva (extinde) o interfata se foloseste tot cuvantul-cheie extends, la fel ca in cazul definitiilor de clasa: public interface <nume_interfata> extends <nume_superinterfata> { .. }
3/2/2014 25

Nota: In cazul interfetelor nu exista o radacina comuna a arborelui de derivare asa cum exista pentru arborele de clasa, clasa Object. Interfetele pot exista independent sau pot mosteni o alta interfata. Ierarhia de interfete este cu mostenire multipla. O singura interfata poate mosteni oricate clase are nevoie.

Implementarea de componente generice


Unul dintre scopurile principale ale programarii orientate pe obiecte este suportul pentru reutilizarea codului. Unul dintre mecanismele importante folosite pentru indeplinirea acestui scop este programarea generica. Programarea generica ne spune ca, daca implementarea unei metode este identica pentru mai multe clase (cu exceptia superclasei) se poate folosi o implementare generica a metodei. De exemplu, putem defini o metoda care sa sorteze un sir de elemente. Algoritmul pentru aceasta metoda este independent de tipul de obiecte care sunt sortate. Se poate deci folosi un algoritm generic. In Java programarea generica este implementata folosind conceptele de baza ale mostenirii si interfetele.
3/2/2014 26

Vom prezenta doua exemple care evidentiaza cum pot fi implementate metode si clase generice folosind principiile de baza ale mostenirii. 1. Un prim exemplu este problema sortarii. Cum putem sorta un sir generic, care sa nu depinda de tipul obiectelor supuse ordonarii? Problema se poate rezolva cu ajutorul unei interfete in care este declarata o metoda de comparare a doua obiecte, care are un parametru de tipul interfetei. In acest fel se pot crea parametrii generici care functioneaza pentru orice clasa care ar folosi aceasta interfata. Exemplul urmator (programul Sort.java) prezinta o metoda de sortare generica denumita sortareComp() care foloseste ca parametru un tablou de obiecte de tip Compara. Tipul Compara este definit de interfata Compara prezentata deja in lectie.
/* Clasa generica pentru sortarea unor obiecte comparabile *----------------------Metode publice------------------*void sortareComp - -> sorteaza un sir de obiecte de tip Compara */ package clasegenerice; public final class Sort { public static void sortareComp(Compara [] a) { Compara temp;
3/2/2014 27

for (int i = 0; i <= a.length - 2; i++) { for (int j = i+1; j <= a.length - 1; j++) { if (a[i].comparaCu(a[j]) == 1) { temp = a[i]; a[i] = a[j]; a[j] = temp; } } } } }

Trebuie remarcat ca numai clasele care implementeaza interfata Compara pot fi sortate astfel. De aceea, pentru a folosi metoda pentru ordonarea unui tablou de forme geometrice de tip FormaGeo clasa FormaGeo trebuie sa implementeze interfata Compara care declara metoda comparaCu(), care la randul ei este folosita de metoda sortareComp() din clasa Sort. In implementarea metodei din interfata (in cazul nostru comparaCu()) intr-o clasa, putem converti prin cast parametrul generic de tipul interfetei Compara in obiectul corespunzator.
3/2/2014 28

Secventa de cod de mai jos prezinta implementarea metodei comparaCu() in clasa FormaGeo si conversia explicita la tipul FormaGeo a unui obiect de tip Compara: public int comparaCu(Compara rhs) { if (arie() < ((FormaGeo) rhs).arie()) return -1; else if (arie() == ((FormaGeo) rhs).arie()) return 0; else return 1; Exemplul urmator (TestFormaInterfata.java) arata cum poate fi folosita metoda generica de sortare denumita sortareComp() pentru sortarea unui tablou de forme geometrice:

3/2/2014

29

import java.io.* ; import clasegenerice.*; import formegeometrice.*; class TestFormaInterfata { private static BufferedReader in; public static void main(String[] args) throws IOException { //Citeste numarul de figuri in = new BufferedReader(new InputStreamReader(System.in)); System.out.print("Numarul de figuri: "); int numForme = Integer.parseInt( in.readLine()); //citeste formele FormaGeo[] forme = new FormaGeo[numForme]; for (int i = 0; i < numForme; ++i) { forme[i] = citesteForma(); } //sortare si afisare Sort.sortareComp(forme);
3/2/2014 30

decat " + forme[i]);

System.out.println("Sortarea dupa arie: "); for (int i = 0; i < numForme; ++i) { System.out.println(forme[i]); } for (int i = 1; i < numForme; ++i) { if (forme[i-1].comparaCu(forme[i]) == -1) System.out.println(forme[i-1] + " are o arie mai mica else System.out.println(forme[i-1] + " are o arie egala cu }

sau mai mare ca " + forme[i]);

}
//creaza un obiect adecvat de tip FormaGeo functie de //datele de intrare. //utilizatorul introduce 'c', 'p' sau 'd' pentru a indica //forma, apoi introduce dimensiunile //in caz de eroare se intoarce un cerc de raza 0 private static FormaGeo citesteForma() throws IOException { double rad;
3/2/2014 31

double lg; double lat; String s; System.out.println("Introduceti tipul formei: "); do { s = in.readLine(); } while (s.length() == 0); switch (s.charAt(0)) { case 'c': System.out.println("Raza cercului: "); rad = Integer.parseInt(in.readLine()); return new Cerc(rad); case 'p': System.out.println("Latura patratului: "); lg = Integer.parseInt(in.readLine()); return new Patrat(lg); case 'd': System.out.println("Lungimea si latimea " + " dreptunghiului pe linii separate: "); lg = Integer.parseInt(in.readLine()); lat= Integer.parseInt(in.readLine()); return new Dreptunghi(lg, lat); default: System.err.println("Tastati c, p sau d: "); return new Cerc(0); }}}
3/2/2014 32

2. Un al doilea exemplu se refera la crearea unei clase generice folosind o superclasa adecvata, cum ar fi Object. In java, daca o clasa nu extinde o alta clasa, atunci ea extinde implicit clasa Object. Rezulta ca, fiecare clasa este o subclasa a lui Object. De exemplu, structura de date abstracta stiva poate fi descrisa prin intermediul unei interfete denumita Stiva. Clasa generica StivaAr care implementeaza aceasta interfata defineste toate metodele specificate in interfata Stiva. Clasa StivaAr foloseste obiecte de tip Object, deci poate sa stocheze orice fel de obiecte. Interfata Stiva descrie functiile disponibile; clasa concreta StivaAr trebuie sa defineasca aceste functii. Aceasta abordare, separarea interfetei de implementarea ei, este o parte fundamentala a orientarii pe obiecte. Programatorul care foloseste, de exemplu, structura de date stiva nu trebuie sa vada implementarea ei, ci doar operatiile admisibile. Aceasta tine de partea de ascundere a informatiei din programarea orientata pe obiecte. De asemenea, folosirea interfetelor mai are si avantajul ca interfetele fiind fixate, o serie de inlocuiri facute in implementarile lor nu necesita practic nici o modificare in programele care utilizeaza aceste interfete.

3/2/2014

33

Sa ne reamintim ce este structura de date abstracta stiva. O stiva este o structura de date in care accesul este permis doar pentru ultimul element inserat. Comportamentul unei stive este foarte asemanator cu cel al unei gramezi de farfurii. Ultima farfurie adaugata va fi plasata in varf fiind, in consecinta, usor de accesat, in timp ce farfuriile puse cu mai mult timp in urma (aflate sub alte farfurii) vor fi mai greu de accesat, putand periclita stabilitatea intregii gramezi. Deci, stiva este adecvata in situatiile in care avem nevoie sa accesam doar elementul din varf. Toate celelalte elemente sunt inaccesibile. Cele trei operatii care se pot face cu structura de date stiva sunt: - inserare - denumita push; - stergere - denumita pop; - cautare - denumita top.

Interfata Java, pentru o structura de tip stiva, denumita Stiva, declara operatiile permise pe stiva si este prezentata in continuare (programul Stiva.java).

3/2/2014

34

/* Interfata pentru o stiva. Stiva expune metode pentru

* manipularea (adaugarea, stergerea, consultarea)


* elementului din varful ei*/ package clasegenerice; public interface Stiva {

public void push(Object x);


public void pop(); public Object top(); public Object topAndPop();

public boolean isEmpty();


public void makeEmpty(); public boolean isFull(); }

Nota: Interfata declara si metodele: topAndPop() care combina doua operatii, consultare si extragere; isEmpty() care verifica daca stiva este goala; makeEmpty() care goleste stiva; isFull() care verifica daca stiva este plina.
3/2/2014 35

Clasa generica StivaAr stocheaza elementele de tip Object (orice tip de obiecte) ale stivei intr-un tablou unidimensional. Clasa StivaAr implementeaza interfata Stiva. Ea foloseste un tablou unidimensional pentru a retine elementele din stiva. Mai exista si alte modalitati de implementare a unei stive, cum ar fi listele inlantuite. Codul sursa al clasei StivaAr (program StivaAr.java) se prezinta in continuare:
/* Implementarea unei stive folosind un tablou */ package clasegenerice; public final class StivaAr implements Stiva { /* Tablou care retine elementele stivei*/ private Object[] elemStiva; /*Dimensiunea maxima a stivei*/ private static int dimMax; /*Pozitia varfului stivei */ private int pozitieVarf; /* Constructor care aloca memorie pentru elemStiva si * initializeaza varful stivei */ public StivaAr(int e) { dimMax = e; elemStiva = new Object[dimMax]; pozitieVarf = -1; }
3/2/2014 36

/* Incrementeaza indicele pozitieVarf si adauga elementul x in stiva.*/ public void push(Object x) { elemStiva[++pozitieVarf] = x; } /* Extrage elementul din varful stivei si apoi decrementeaza indicele pozitieVarf. */ public void pop() { pozitieVarf--; } /* Returneaza elementul din varful stivei * (ultimul adaugat).*/ public Object top() { return elemStiva[pozitieVarf]; } /* Returneaza elementul din varful stivei si * il elimina apoi din stiva.*/ public Object topAndPop() { return elemStiva[pozitieVarf--]; }
3/2/2014 37

/* Verifica daca stiva e vida. */ public boolean isEmpty() { return (pozitieVarf == -1); } /* Elimina toate elementele din stiva. */ public void makeEmpty() { pozitieVarf = -1; } /* Verifica daca stiva este plina */ public boolean isFull() { return (pozitieVarf == dimMax - 1); }
}

Programul urmator (TestStiva.java) prezinta un exemplu de utilizare a structurii de tip stiva in care se foloseste implementarea din clasa StivaAr a interfetei Stiva. Acest program afiseaza 10 numere naturale, stocate intr-un tablou, in ordine inversa folosind structura de tip stiva.

3/2/2014

38

/* Clasa de test simpla pentru o stiva, care adauga 10 numere * dupa care le extrage in ordine inversa */ import clasegenerice.*; public class TestStiva { public static void main(String[] args) { Stiva s = new StivaAr(10); //introducem elemente in stiva for (int i = 0; i < 10; i++) { s.push(new Integer(i)); } // Verificam daca stiva este plina if (s.isFull()) System.out.println("Stiva este plina"); // Aflam elementul din varful stivei int elemVarf = ((Integer) s.top()).intValue(); System.out.println("Elementul din varful stivei este:" + elemVarf); //scoatem elementele din stiva si le afisam System.out.print("Continutul stivei este: "); while (!s.isEmpty()) {System.out.print(s.topAndPop() + " ");} } }
3/2/2014 39

S-ar putea să vă placă și