capitolul 3 poo

52
1 CAPITOLUL 3. POLIMORFISM ŞI GENERICITATE 3 3.1 MOŞTENIRE ÎN STRUCTURA OBIECTELOR 4 3.1.1 MOŞTENIREA ATRIBUTELOR 4 3.1.2 SUPRA-SCRIERE ŞI SUPRA-ÎNCĂRCARE STRUCTURALĂ 6 3.2 MOŞTENIRE COMPORTAMENTALĂ 10 3.2.1 MOŞTENIREA OPERAŢIILOR ŞI SUBTIPIZAREA 10 3.2.2 SUPRA-SCRIERE ŞI SUPRA-ÎNCĂRCARE COMPORTAMENTALĂ 14 3.2.3 CLASE ŞI METODE ABSTRACTE 16 3.3 POLIMORFISM, SUBTIPIZARE ŞI INTERFEŢE 19 3.3.1 SUBTIPIZARE ŞI INTERFEŢE 20 3.3.2 MOŞTENIRE MULTIPLĂ 21 3.3.3 FORME DE POLIMORFISM 22 3.4 EGALITATEA ŞI COMPARABILITATEA OBIECTELOR 26 3.4.1 EGALITATEA ŞI IDENTITATEA OBIECTELOR. OPERAŢIA EQUALS DIN SUPERCLASA OBJECT 27 3.4.2 OBIECTE COMPARABILE ŞI OBIECTE COMPARATORI. INTERFEŢELE COMPARABLE ŞI COMPARATOR 29 3.5 OBIECTE ÎN COLECŢII. COLECŢII GENERICE 31 3.5.1 API PENTRU COLECŢIILE DE OBIECTE ÎN MEDIUL JAVA. COLECŢII INDEXATE, SORTATE ŞI TIPIZATE 32 3.5.2 COLECŢII TIPIZATE ŞI STRUCTURI DE CONTROL PENTRU PARCURGEREA COLECŢIILOR 38 3.5.3 ELEMENTE SPECIFICE CLASELELOR DE IMPLEMENTARE A INTERFEŢELOR STANDARD PRIVIND COLECŢIILE 41

Upload: andrei-sofrone

Post on 29-Jun-2015

173 views

Category:

Documents


4 download

TRANSCRIPT

Page 1: Capitolul 3 POO

1

CAPITOLUL 3. POLIMORFISM ŞI GENERICITATE 3

3.1 MOŞTENIRE ÎN STRUCTURA OBIECTELOR 4 3.1.1 MOŞTENIREA ATRIBUTELOR 4 3.1.2 SUPRA-SCRIERE ŞI SUPRA-ÎNCĂRCARE STRUCTURALĂ 6 3.2 MOŞTENIRE COMPORTAMENTALĂ 10 3.2.1 MOŞTENIREA OPERAŢIILOR ŞI SUBTIPIZAREA 10 3.2.2 SUPRA-SCRIERE ŞI SUPRA-ÎNCĂRCARE COMPORTAMENTALĂ 14 3.2.3 CLASE ŞI METODE ABSTRACTE 16 3.3 POLIMORFISM, SUBTIPIZARE ŞI INTERFEŢE 19 3.3.1 SUBTIPIZARE ŞI INTERFEŢE 20 3.3.2 MOŞTENIRE MULTIPLĂ 21 3.3.3 FORME DE POLIMORFISM 22 3.4 EGALITATEA ŞI COMPARABILITATEA OBIECTELOR 26 3.4.1 EGALITATEA ŞI IDENTITATEA OBIECTELOR. OPERAŢIA EQUALS DIN SUPERCLASA OBJECT

27 3.4.2 OBIECTE COMPARABILE ŞI OBIECTE COMPARATORI. INTERFEŢELE COMPARABLE ŞI

COMPARATOR 29 3.5 OBIECTE ÎN COLECŢII. COLECŢII GENERICE 31 3.5.1 API PENTRU COLECŢIILE DE OBIECTE ÎN MEDIUL JAVA. COLECŢII INDEXATE, SORTATE ŞI

TIPIZATE 32 3.5.2 COLECŢII TIPIZATE ŞI STRUCTURI DE CONTROL PENTRU PARCURGEREA COLECŢIILOR 38 3.5.3 ELEMENTE SPECIFICE CLASELELOR DE IMPLEMENTARE A INTERFEŢELOR STANDARD

PRIVIND COLECŢIILE 41

Page 2: Capitolul 3 POO

2

Page 3: Capitolul 3 POO

CAPITOLUL 3

Polimorfism şi genericitate

În capitolul anterior am arătat, în principal, cum pot fi construite şi

create obiectele. În acest context, au fost discutate conceptul fundamental de clasă şi două principii esenţiale: principiul încapsulării şi cel al reutilizării. Aceste elemente sunt definitorii în programarea orientată obiect, însă avantajul esenţial al acesteia este relevat de posibilitatea construirii componentelor software într-o manieră generică, astfel încât evoluţia acestora în funcţionalitate să se poată face cât mai natural şi eficient, fără improvizaţii şi costuri deosebite, asigurându-le o durată de utilizare „eficientă” cât mai îndelungată, iar înlocuirea lor să se poată face într-un mod cât mai neutru pentru angrenajul aplicaţiei din care fac parte. Acest lucru este posibil datorită a două aspecte specifice, în mod natural, paradigmei obiectelor: polimorfismul, ceea ce înseamnă că acelaşi element ar putea juca mai multe roluri sau ar putea fi interpretat în mai multe feluri, şi genericitate, ceea ce se traduce în posibilităţile de parametrizare a cât mai multor aspecte legate de componentele claselor, atribute și operații, dar şi de parametrizarea claselor în sine, ceea ce se numește, de fapt, parametrizarea tipurilor.

Page 4: Capitolul 3 POO

Capitolul 3

4

3.1 Moştenire în structura obiectelor Moştenirea, în special prin efectul ei concretizat prin sub-tipizare, este

factorul esenţial în manifestarea polimorfismului. Aşa cum deja am discutat, în principiu, în capitolul anterior,

moştenirea este înţeleasă, în general, ca mecanismul prin care o anumită clasă (subclasă) poate fi implicit definită având la bază atributele şi operaţiile altei clase (superclasa).

Modul în care structura, adică ansamblul acelor componente interne implementate ca variabile de instanţă, este moştenită, comportă totuşi câteva aspecte particulare.

3.1.1 Moştenirea atributelor După cum am afirmat deja, prin specializare în subclase obiectele

subclasate moştenesc atributele (variabilele de instanţă) care formează scheletul structural al claselor părinte.

Accesul din contextul subclaselor la variabilele de instanţă care provin

din superclase este dependent de specificatorii de vizibilitate: prin specificatorul public se desemnează faptul că atributul

(variabila de instanţă) va fi accesibil la nivelul subclaselor, la fel ca şi la nivelul oricărei alte clase care accesează clasa iniţială prin referenţiere obişnuită (compunere şi parametrizare);

vizibilitatea „friendly” sau internă la nivel de pachet desemnată, de fapt, prin lipsa unui specificator explicit are un efect asemănător cu folosirea specificatorului public restrâns însă numai la clasele din același spaţiu (sau pachet);

prin specificatorul private se desemnează faptul că, deşi făcând parte structural din obiectele subclasei, componentele interne implementate ca variabile de instanță nu mai sunt accesbile în mod direct, iniţializarea lor făcându-se cel mult prin apelarea unui constructor din clasa părinte, folosind cuvântul-cheie super;

prin specificatorul protected se desemnează faptul că membrul intern, materializat printr-o variabilă de instanță, este accesibil obiectelor subclaselor, domeniul de vizibilitate sau access fiind limitat însă numai la subclase.

Un lucru esenţial, care decurge din folosirea cuvântului-cheie super pe

care l-am subliniat mai sus, se referă la faptul că mecanismul de iniţializare a instanţelor din subclase trebuie să permită şi iniţializarea variabilelor de instanţă moştenite, eventual chiar la nivelul superclaselor.

Page 5: Capitolul 3 POO

Polimorfism și genericitate

5

// Listing 3.1: Iniţializare membri moşteniţi,

// prin invocare constructor clasă părinte – super

// Superclasa InregistrareContabilă

public class InregistrareContabila {

private Integer id;

private Integer nrOrdine;

protected String tip; // discriminator debit, credit

private Double suma;

private Cont cont;

private OperatiuneContabila operatiune;

public Integer getId() {

return id;

}

public void setId(Integer id) {

this.id = id;

}

... ... ...

// constructori (nu se moştenesc)

public InregistrareContabila(Integer id, Cont cont,

Double suma){

this.id = id;

this.tip = this.getClass().getSimpleName();

this.suma = suma;

this.cont = cont;

}

public InregistrareContabila() {

}

}

// Subclase specializate InregistrareDebit şi InregistrareCredit -

// ----------------

public class InregistrareDebit extends InregistrareContabila{

private Integer nrOrdineDebit;

public int getNrOrdineDebit() {

return nrOrdineDebit;

}

... ... ...

// invocare constructor părinte – super

// acces (prin protected) la membru moştenit

public InregistrareDebit(Integer id, Cont cont,

Double suma) {

super(id, cont, suma);

Page 6: Capitolul 3 POO

Capitolul 3

6

this.tip = "Debit”;

}

}

//------------------------------------

public class InregistrareCredit extends InregistrareContabila{

private Integer nrOrdineCredit;

public Integer getNrOrdineCredit() {

return nrOrdineCredit;

}

... ... ...

public InregistrareCredit(Integer id, Cont cont,

Double suma) {

super(id, cont, suma);

this.tip = "Credit”;

}

}

3.1.2 Suprascriere şi supraîncărcare structurală Scopul subclasării ar putea fi motivat de efortul de adaptare a unei

clase existente, cu o funcţionalitate dată, la un nou context mai specific. Această nevoie de adaptare este legată și de faptul că unele aspecte moştenite nu corespund în totalitate cu noul context. Ceea ce duce la necesitatea sau, cel puţin, tentaţia redefinirii anumitor aspecte preluate de la clasele părinte.

În cazul membrilor-variabile de instanţă însă, subclasele nu pot

redefini atributele definite la nivelul superclaselor (chiar dacă le pot accesa). Este tutuși posibil ca la nivelul subclaselor numele atributelor moștenite din superclase să poată fi supraîncărcate cu cele ale unor atribute proprii, adică: pot fi definite în subclase atribute ale căror nume se regăsesc şi la

nivelul superclaselor; diferenţierea între atributele ale căror nume sunt supraîncărcate se

poate face prin cuvintele-cheie super şi this.

// Listing 3.2: Supraîncărcare structurală:

// importanţa cuvântului cheie this

// Superclasa InregistrareContabilă

public class InregistrareContabila {

private Integer id;

protected Integer nrOrdine;

Page 7: Capitolul 3 POO

Polimorfism și genericitate

7

protected String tip; // discriminator debit, credit

private Double suma;

private Cont cont;

private OperatiuneContabila operatiune;

... ... ...

// proprietatea ce încapsulează nrOrdine

public Integer getNrOrdine() {

return nrOrdine;

}

public void setNrOrdine(Integer nrOrdine) {

this.nrOrdine = nrOrdine;

}

... ... ...

}

// Subclase specializate InregistrareD şi InregistrareC --------

public class InregistrareC extends InregistrareContabila{

// membru-variabilă supraîncărcată

private Integer nrOrdine;

// proprietate care încapsulează

// membru-variabilă supraîncărcată

public Integer getNrOrdineCredit() {

return this.nrOrdine;

}

public void setNrOrdineCredit(Integer nrOrdineCredit) {

this.nrOrdine = nrOrdineCredit;

}

public InregistrareC() {

}

public InregistrareC(Integer id, Cont cont, Double suma) {

super(id, cont, suma);

}

}

//------------------------------------

public class InregistrareD extends InregistrareContabila{

// membru-variabilă supraîncărcată

private Integer nrOrdine;

// proprietate care încapsulează

// membru-variabilă supraîncărcată

public Integer getNrOrdineDebit() {

return this.nrOrdine;

}

public void setNrOrdineDebit(Integer nrOrdineDebit) {

Page 8: Capitolul 3 POO

Capitolul 3

8

this.nrOrdine = nrOrdineCredit;

}

public InregistrareD() {

}

public InregistrareD(Integer id, Cont cont, Double suma) {

super(id, cont, suma);

}

}

//------------------------------------

public class TestInregistrareContabilaSuprascriereStructurala {

public static void main(String[] args){

InregistrareContabila inregistrareDebit =

new InregistrareContabila();

inregistrareDebit.setTip("Debit");

inregistrareDebit.setNrOrdine(1);

InregistrareD iDebit = new InregistrareD();

InregistrareC iCredit = new InregistrareC();

iDebit.setNrOrdineDebit(1);

iCredit.setNrOrdineCredit(1);

System.out.println("iDebit.nrOrdineDebit: "

+ iDebit.getNrOrdineDebit());

System.out.println("iDebit.nrOrdine: "

+ iDebit.getNrOrdine());

}

}

Auroreferenţierea

Cuvântul-cheie this, a cărui semnificație ar putea fi înțeleasă ca „acest

obiect” sau „obiectul curent”, poate fi utilizat numai în cadrul unei metode non-statice şi produce referinţa către obiectul curent ale cărui metode ar putea urma a fi astfel apelate. Apelul unei anumite operaţii, în cadrul unei alte metode aparţinând aceluiaşi obiect, nu implică folosirea obligatorie a cuvântului-cheie this. Acest cuvânt este folosit, în special, în cazul supraîncărcării structurale sau pentru instrucţiunea return care trebuie să returneze referinţa obiectului curent.

De asemenea, this poate fi apelat din interiorul unui constructor, caz

în care este folosit sub forma unui apel cu argumente – this (arg) – şi va invoca în mod explicit constructorul corespunzător listei de argumente.

Page 9: Capitolul 3 POO

Polimorfism și genericitate

9

Există posibilitatea ca în interiorul unui constructor să fie apelat un alt constructor. În acestă situație cuvântul-cheie this este folosit pentru a apela ceilalţi constructori.

// Listing 3.3: Invocarea constructorilor prin super() şi this()

// Superclasa InregistrareContabilă

public class InregistrareContabila {

private Integer id;

private Integer nrOrdine;

protected String tip; // discriminator debit, credit

protected Double suma;

private Cont cont;

private OperatiuneContabila operatiune;

... ... ...

// constructori

public InregistrareContabila() {

}

public InregistrareContabila(Integer id, Cont cont){

this.id = id;

this.tip = this.getClass().getSimpleName();

this.cont = cont;

}

public InregistrareContabila(Integer id, Cont cont,

Double suma){

this(id, cont);

this.suma = suma;

}

}

// Subclase specializate InregistrareDebit şi InregistrareCredit –

public class InregistrareDebit extends InregistrareContabila{

private Integer nrOrdineDebit;

public int getNrOrdineDebit() {

return nrOrdineDebit;

}

... ... ...

// constructori

public InregistrareDebit() {

}

public InregistrareDebit(Integer id, Cont cont) {

super(id, cont);

}

public InregistrareDebit(Integer id, Cont cont, Double suma) {

Page 10: Capitolul 3 POO

Capitolul 3

10

this(id, cont);

this.suma = suma;

}

}

//------------------------------------

public class InregistrareCredit extends InregistrareContabila{

private Integer nrOrdineCredit;

public Integer getNrOrdineCredit() {

return nrOrdineCredit;

}

... ... ...

// constructori

public InregistrareCredit() {

}

public InregistrareCredit(Integer id, Cont cont) {

super(id, cont);

}

public InregistrareCredit(Integer id, Cont cont, Double suma) {

this(id, cont);

this.suma = suma;

}

}

3.2 Moştenire comportamentală Acest tip de moştenire are în vedere preluarea operaţiilor şi metodelor

de implementare în structura comportamentală a subclaselor. Esenţial este faptul că, în procesul de moştenire, operaţiile (specificaţiile) sunt considerate în mod obligatoriu preluate la nivelul subclaselor, metodele concrete de implementare ale acestora putând fi însă redefinite. Cu alte cuvinte, natura tipului este în mod sigur păstrată prin moştenire, acest lucru însemnând că se are în vedere subtipizarea, nu şi implementarea lui.

3.2.1 Moştenirea operaţiilor şi subtipizarea Prin specializare, obiectele subclaselor moştenesc operaţiile care

descriu comportamentul claselor părinte. Cu alte cuvinte, subclasele trebuie să respectate întocmai semnătura acestora: nume + tip + argument + specificator vizibilitate + tipuri excepţii.

Page 11: Capitolul 3 POO

Polimorfism și genericitate

11

Invocarea internă sau accesul din contextul subclaselor, a operaţiilor care provin din superclase este însă dependentă de specificatorii de vizibilitate: public; (package); protected;

cu aceeaşi semantică ca şi în cazul membrilor-variabile de instanţă (vezi paragraful 3.1.1).

Moştenirea are ca efect implicit subtipizarea, adică obiectele din subclase au aceleaşi caracteristici şi se comportă similar cu cele din superclase. Pentru ca relaţia de generalizare subclasă superclasă să fie semantic corectă, aserţiunea <o instanţă subclasă> [este gen sau fel de/is a kind of] <o instanţă superclasă> trebuie să fie, de asemenea, adevarată.

// Listing 3.4: Invocarea operaţiilor moştenite şi subtipizare

// Superclasa OperatiuneContabila

public class OperatiuneContabila {

private Integer idOperatiune;

private Date dataContabilizare;

protected Map<Integer, InregistrareContabila> inregistrari =

new TreeMap<Integer, InregistrareContabila>();

// operaţii ale proprietăţilor ce vor fi moştenite

public Integer getIdOperatiune() {

return idOperatiune;

}

public void setIdOperatiune(Integer idOperatiune) {

this.idOperatiune = idOperatiune;

}

public Date getDataContabilizare() {

return dataContabilizare;

}

public void setDataContabilizare(Date dataContabilizare) {

this.dataContabilizare = dataContabilizare;

}

public List<InregistrareContabila> getInregistrari() {

List<InregistrareContabila> result =

new ArrayList<InregistrareContabila>();

result.addAll(inregistrari.values());

return result;

}

Page 12: Capitolul 3 POO

Capitolul 3

12

// operaţii şi metode ce urmează a fi moştenite

public void addInregistrareContabila(

InregistrareContabila inregistrare){

TreeSet<Integer> cheiInregistrari =

new TreeSet<Integer>();

cheiInregistrari.addAll(inregistrari.keySet());

if (cheiInregistrari.size() > 0)

inregistrare.setNrOrdine(cheiInregistrari.last() + 1);

else

inregistrare.setNrOrdine(1);

inregistrare.setOperatiune(this);

this.inregistrari.put(inregistrare.getNrOrdine(),

inregistrare);

}

public Double getSold(){

return getDebit() - getCredit();

}

public Double getDebit(){

Double debit = 0.0;

for (InregistrareContabila i: this.getInregistrari()){

if (i instanceof InregistrareDebit)

debit += i.getSuma();

}

return debit;

}

public Double getCredit(){

Double credit = 0.0;

for (InregistrareContabila i: this.getInregistrari()){

if (i instanceof InregistrareCredit)

credit += i.getSuma();

}

return credit;

}

}

//----------------------------------------------------------------

//Subclase care vor moşteni

//----------------------------------------------------------------

public abstract class OperatiuneComerciala

extends OperatiuneContabila{

private String partener;

public String getPartener() {

return partener;

}

Page 13: Capitolul 3 POO

Polimorfism și genericitate

13

public void setPartener(String partener) {

this.partener = partener;

}

public OperatiuneComerciala() {}

public OperatiuneComerciala(Integer idOperatiune,

Date dataContabilizare) {

super(idOperatiune, dataContabilizare);

}

public OperatiuneComerciala(Integer idOperatiune,

Date dataContabilizare, String partener) {

super(idOperatiune, dataContabilizare);

this.partener = partener;

}

}

//----------------------------------------------------------------

public class Vinzare extends OperatiuneComerciala{

private Integer nrFacturaEmisa;

private Date dataFacturaEmisa;

public Vinzare() {

}

public Vinzare(Integer idOperatiune, Date dataContabilizare,

String partener) {

super(idOperatiune, dataContabilizare, partener);

}

public Vinzare(Integer idOperatiune,

Date dataContabilizare, String partener,

Integer nrFacturaEmisa, Date dataFacturaEmisa) {

super(idOperatiune, dataContabilizare, partener);

this.nrFacturaEmisa = nrFacturaEmisa;

this.dataFacturaEmisa = dataFacturaEmisa;

}

public Date getDataFacturaEmisa() {

return dataFacturaEmisa;

}

public void setDataFacturaEmisa(Date dataFacturaEmisa) {

this.dataFacturaEmisa = dataFacturaEmisa;

}

public Integer getNrFacturaEmisa() {

return nrFacturaEmisa;

}

public void setNrFacturaEmisa(Integer nrFacturaEmisa) {

this.nrFacturaEmisa = nrFacturaEmisa;

}

Page 14: Capitolul 3 POO

Capitolul 3

14

}

//----------------------------------------------------------------

// Test subtipizare

//----------------------------------------------------------------

public class TestSubtipizareOperatiuni {

public static void main(String[] args){

OperatiuneContabila op_1 =

new OperatiuneContabila(1,new Date());

Vinzare op_2 = new Vinzare(2, new Date(),"Alfa SRL");

// apel metoda proprie getSold()

System.out.println("" + op_1.getSold());

// apel metoda mostenita getSold()

System.out.println("" + op_2.getSold());

}

}

3.2.2 Suprascriere şi supraîncărcare comportamentală Deşi, în privinţa operaţiilor, nu se fac „compromisuri”, în privinţa

moştenirii metodelor concrete de implementare lucrurile sunt mai flexibile: la nivelul unei subclase, o metodă moştenită poate fi redefinită (sau suprascrisă): implementarea unei operaţii poate fi schimbată în subclase. Acest lucru este posibil pentru că modificarea metodelor de implementare nu afectează subtipizarea.

Un alt fenomen posibil ca urmare a moştenirii (însă nu exclusiv) este

supraîncărcarea: la nivelul unei subclase poate fi definită o operaţiune cu acelaşi nume ca al altei operaţiuni moştenite, dar mod de parametrizare diferit. Ca urmare, instanţele subclasei pot fi invocate prin acelaşi nume de operaţie, dar care, funcţie de numărul şi tipul argumentelor, vor fi mapate pe implementări comportamentale distincte. Regulile de invocare în contextul supraîncărcării operaţiilor le-am făcut deja cunoscute în capitolul anterior.

// Listing 3.5: Supraîncărcare operaţii moştenite:

// extindere exemplu din Listing 3.4

public class OperatiuneContabila {

... ... ...

public List<InregistrareContabila> getInregistrari() {

List<InregistrareContabila> result =

new ArrayList<InregistrareContabila>();

result.addAll(inregistrari.values());

Page 15: Capitolul 3 POO

Polimorfism și genericitate

15

return result;

}

public void addInregistrareContabila(

InregistrareContabila inregistrare){

TreeSet<Integer> cheiInregistrari =

new TreeSet<Integer>();

cheiInregistrari.addAll(inregistrari.keySet());

if (cheiInregistrari.size() > 0)

inregistrare.setNrOrdine(cheiInregistrari.last() + 1);

else

inregistrare.setNrOrdine(1);

inregistrare.setOperatiune(this);

this.inregistrari.put(inregistrare.getNrOrdine(),

inregistrare);

}

... ... ...

}

//----------------------------------------------------------------

//Subclase care vor moşteni

//----------------------------------------------------------------

public abstract class OperatiuneComerciala

extends OperatiuneContabila{

... ... ...

// supraincarcate getInregistrari()

// si invocare operatiune supraincarcata

public List<InregistrareContabila> getInregistrari(

String tip) {

List<InregistrareContabila> result =

new ArrayList<InregistrareContabila>();

if(tip.equals("Debit")){

for (InregistrareContabila i : getInregistrari())

if (i instanceof InregistrareDebit)

result.add(i);

}

if(tip.equals("Credit")){

for (InregistrareContabila i : getInregistrari())

if (i instanceof InregistrareCredit)

result.add(i);

}

return result;

}

Page 16: Capitolul 3 POO

Capitolul 3

16

... ... ...

}

//----------------------------------------------------------------

public class Vinzare extends OperatiuneComerciala{

... ... ...

// suprascriere

@Override

public void addInregistrareContabila(

InregistrareContabila inregistrare){

if (inregistrare instanceof InregistrareDebit &&

! inregistrare.getCont().getCod().startsWith("4"))

throw new AppException(

"Inregistrare incompatibila cu operatiune vinzare !");

super.addInregistrareContabila(inregistrare);

}

... ... ...

}

3.2.3 Clase şi metode abstracte Există posibilitatea ca la nivelul unor superclase (definite ca abstracte)

să fie doar definite anumite operaţii, subclasele concrete ale acestora având sarcina asocierii unei implementări corespunzătoare.

Există situaţii în care o clasă nu este creată în mod necesar pentru a fi

instanţiată direct, motiv pentru care ar trebui declarată ca abstractă. Prin urmare, scopul ei este de a servi drept fundaţie pentru construirea subclaselor, permiţându-se astfel ca obiecte diferite, dar care se aseamănă între ele în anumite privinţe, să fie manipulate într-o manieră unitară, prin intermediul specificaţiilor superclaselor comune. Invers, o clasă prin care sunt create nemijlocit instanţe este numită concretă.

În această privinţă, în limbajul Java se utilizează, pentru a face

diferenţa între clasele fără posibilităţi directe de instanţiere şi celelalte, cuvântul-cheie abstract :

public abstract class OperatiuneComerciala

extends OperatiuneContabila{ ... ... ...}

Page 17: Capitolul 3 POO

Polimorfism și genericitate

17

Operațiile definite, dar neimplementate, se mai numesc operații abstracte. Prin urmare, o operație abstractă este o operație specificată, dar neimplementată. Datorită faptului că o clasă concretă nu poate conţine nici o operație/metodă abstractă, înseamnă că orice clasă concretă trebuie să asigure implementarea operațiilor abstracte specificate în superclasele abstracte.

public abstract List<InregistrareContabila> getInregistrari(

String tip);

Se observă, în specificația operațiilor abstracte, lipsa simbolurilor de

marcare ale blocului de instrucţiuni executabile {...} care ar trebui să formeze corpul metodei, dar pe care (sub)clasele concrete sunt obligate să-l implementeze. // Listing 3.6: Implementare operaţii abstracte moştenite:

// modificare Listing 3.5

public class OperatiuneContabila {

... ... ...

public List<InregistrareContabila> getInregistrari() {

List<InregistrareContabila> result =

new ArrayList<InregistrareContabila>();

result.addAll(inregistrari.values());

return result;

}

public void addInregistrareContabila(

InregistrareContabila inregistrare){

TreeSet<Integer> cheiInregistrari =

new TreeSet<Integer>();

cheiInregistrari.addAll(inregistrari.keySet());

if (cheiInregistrari.size() > 0)

inregistrare.setNrOrdine(cheiInregistrari.last() + 1);

else

inregistrare.setNrOrdine(1);

inregistrare.setOperatiune(this);

this.inregistrari.put(

inregistrare.getNrOrdine(), inregistrare);

}

... ... ...

}

//----------------------------------------------------------------

//Subclase care vor moşteni

//----------------------------------------------------------------

// Subclasa abstractă

Page 18: Capitolul 3 POO

Capitolul 3

18

public abstract class OperatiuneComerciala

extends OperatiuneContabila{

... ... ...

// operatie abstractă

public abstract List<InregistrareContabila>

getInregistrari(String tip) ;

... ... ...

}

//----------------------------------------------------------------

// Subclasă concretă

public class Vinzare extends OperatiuneComerciala{

... ... ...

@Override

public void addInregistrareContabila(

InregistrareContabila inregistrare){

if (inregistrare instanceof InregistrareDebit &&

!inregistrare.getCont().getCod().startsWith("411"))

throw new AppException(

"Inregistrare incompatibila cu operatiune vinzare !");

super.addInregistrareContabila(inregistrare);

}

//implementare operaţie abstractă moştenită

public List<InregistrareContabila> getInregistrari(

String tip) {

List<InregistrareContabila> result =

new ArrayList<InregistrareContabila>();

if(tip.equals("Debit")){

for (InregistrareContabila i : getInregistrari())

if (i instanceof InregistrareDebit)

result.add(i);

}

if(tip.equals("Credit")){

for (InregistrareContabila i : getInregistrari())

if (i instanceof InregistrareCredit)

result.add(i);

}

return result;

}

... ... ...

}

//----------------------------------------------------------------

Page 19: Capitolul 3 POO

Polimorfism și genericitate

19

// Test invocare implementări operaţii abstracte

//----------------------------------------------------------------

public class TestImplOperatiuniAbstracte {

public static void main(String[] args){

Cont c_1 = new Cont("411.1", "Client Alfa SRL");

Cont c_2 = new Cont("371", "Marfuri");

OperatiuneContabila op_1 = new OperatiuneContabila(

1,new Date());

op_1.addInregistrareContabila(

new InregistrareDebit(1, c_1, 100.0));

op_1.addInregistrareContabila(

new InregistrareCredit(2, c_2, 100.0));

Vinzare op_2 = new Vinzare(2, new Date(),"Alfa SRL");

op_2.addInregistrareContabila(

new InregistrareDebit(3, c_1, 50.0));

op_2.addInregistrareContabila(

new InregistrareCredit(4, c_2, 50.0));

// apel metoda getInregistrari

// din clasa OperatiuneContabila

System.out.println("op_1: "

+ op_1.getInregistrari().size());

// apel metoda getInregistrari

// supraîncărcată în clasa Vinzare

System.out.println("op_2: "

+ op_2.getInregistrari("Debit").size());

}

}

3.3 Polimorfism, subtipizare şi interfeţe Până în acest moment, am discutat despre mecanismele prin care este

posibilă subtipizarea şi polimorfismul, pornind de la clase abstracte sau concrete. În continuare, ne vom concentra asupra fenomenului în sine, mai exact asupra naturii generice a componentelor software, construite folosind elemente polimorfice.

Mai întâi vom aduce în discuție conceptul de interfaţă ca mecanism

de separare a definirii tipului faţă de implementare. Apoi vom avea în vedere subtipizarea în contextul moştenirii aplicată interfeţelor. În fine,

Page 20: Capitolul 3 POO

Capitolul 3

20

vom trece în revistă formele de polimorfism cele mai relevante: al operaţiilor, al mesajelor şi al variabilelor.

3.3.1 Subtipizare şi interfeţe Interfeţele sunt asemănătoare claselor abstracte în privinţa faptului că

nu pot fi folosite pentru instanţiere directă. Definiţia lor se limitează doar la membri-metode, nu şi variabile de instanţă. Singurele componente structurate de natura „datelor”, care pot fi incluse în definiţia interfeţelor, sunt constantele.

Dacă în privinţa claselor abstracte pot fi omise anumite elemente de

implementare, în privinţa interfeţelor, în declaraţiile lor nu există nici o „urmă” de implementare: definiţia unei interfeţe poate conţine numai specificaţiile operaţiilor (la fel ca şi în cazul metodelor abstracte) şi definiţii de constante.

O clasă poate implementa o interfaţă în mare măsură la fel cum o

clasă abstractă este extinsă (extends) de subclasele sale concrete. Faptul că o clasă se conformează unei interfeţe trebuie declarat în mod explicit prin intermediul cuvântului-cheie implements.

// Listing 3.7: Definire interfeţe şi implementare

// Definire interfaţă

public interface IServiciuOperatiuni {

Double getCredit(OperatiuneContabila o);

Double getDebit(OperatiuneContabila o);

Double getSold(OperatiuneContabila o);

}

// Definite clasă de implementare

public class ServiciuOperatiuni implements IServiciuOperatiuni {

public Double getSold(OperatiuneContabila o){

return getDebit(o) - getCredit(o);

}

public Double getDebit(OperatiuneContabila o){

Double debit = 0.0;

for (InregistrareContabila i: o.getInregistrari()){

if (i instanceof InregistrareDebit)

debit += i.getSuma();

}

return debit;

}

Page 21: Capitolul 3 POO

Polimorfism și genericitate

21

public Double getCredit(OperatiuneContabila o){

Double credit = 0.0;

for (InregistrareContabila i: o.getInregistrari()){

if (i instanceof InregistrareCredit)

credit += i.getSuma();

}

return credit;

}

}

3.3.2 Moştenire multiplă Deosebirea esenţială dintre ierarhiile de subtipizare cu interfeţe şi

ierarhiile simple de clase rezidă în faptul că, în cazul primelor, o clasă poate extinde, de fapt, implementa, mai mult decât un singur părinte-interfaţă.

De asemenea, regulile de subtipizare care se aplică claselor se aplică în aceeaşi măsură şi interfeţelor.

// Listing 3.7: Implementare interfeţe multiple

//----------------------------------------------------------------

// Interfeţe ce urmează a fi implementate

//----------------------------------------------------------------

public interface Validatable {

boolean isValid();

}

//----------------------------------------------------------------

public interface Comparable {

public int compareTo(T o);

}

//----------------------------------------------------------------

// Clasă ce implementează mai multe interfeţe

//----------------------------------------------------------------

public class OperatiuneContabila implements

Comparable <OperatiuneContabila>, Validatable{

... ... ...

public int compareTo(OperatiuneContabila op) {

if (this.getDataContabilizare()

Page 22: Capitolul 3 POO

Capitolul 3

22

.after(op.getDataContabilizare()))

return 1;

if (this.getDataContabilizare()

.before(op.getDataContabilizare()))

return -1;

return 0;

}

public boolean isValid(){

if (getDebit().equals(getCredit()))

return true;

throw new ExceptieValidare(

"Operatiune dezechilibrata: debit # credit!");

}

... ... ...

}

3.3.3 Forme de polimorfism Cele mai relevante forme de polimorfism constau în: polimorfismul unei operaţii, care are în vedere setul de clase în

definiţia cărora este definită respectiva operaţie și se referă la fenomenul prin care aceeași semnătură a unei operații se regăsește în mai multe noduri ale unei ierarhii de moștenire însoțind metodele de suprascriere;

// Listing 3.8: Acelaşi nume de operaţie cu mai multe implementări

//----------------------------------------------------------------

// Ierarhia de moştenire

// operaţie polimorformă addInregistrareContabila

//----------------------------------------------------------------

public interface IOperatieContabila {

void addInregistrareContabila(

InregistrareContabila inregistrare);

Date getDataContabilizare();

List<InregistrareContabila> getInregistrari();

Double getSold();

void removeInregistrareContabila(

InregistrareContabila inregistrare);

Page 23: Capitolul 3 POO

Polimorfism și genericitate

23

}

//----------------------------------------------------------------

public class OperatiuneContabila implements

Comparable<OperatiuneContabila>,

Validatable, IOperatieContabila{

... ... ...

}

//----------------------------------------------------------------

public abstract class OperatiuneComerciala

extends OperatiuneContabila{

... ... ...

}

//----------------------------------------------------------------

// Două implementări diferite pentru

// operaţie polimorformă addInregistrareContabila

//----------------------------------------------------------------

public class Vinzare extends OperatiuneComerciala{

... ... ...

public void addInregistrareContabila(

InregistrareContabila inregistrare){

if (inregistrare instanceof InregistrareDebit

&&! inregistrare.getCont().getCod().startsWith("411"))

throw new AppException(

"Inregistrare incompatibila cu operatiune vinzare !");

super.addInregistrareContabila(inregistrare);

}

... ... ...

}

//----------------------------------------------------------------

public class Cumparare extends OperatiuneComerciala{

... ... ...

public void addInregistrareContabila(

InregistrareContabila inregistrare){

if (inregistrare instanceof InregistrareDebit

&& ! inregistrare.getCont().getCod().startsWith("401"))

throw new AppException(

"Inregistrare incompatibila cu operatiune vinzare !");

super.addInregistrareContabila(inregistrare);

}

... ... ...

}

polimorfismul unei variabile, care are în vedere setul de clase

ale căror instanţe pot fi manevrate/referenţiate prin intermediul

Page 24: Capitolul 3 POO

Capitolul 3

24

acelei variabile, ca urmare a inferenţei tipului asociat variabilei;

// Listing 3.9: Aceeaşi variabilă stocând referinţe

// către instanţe din clase diferite

//----------------------------------------------------------------

public class TestPolimorfismVariabileDeInstanta {

public static void main(String[] args){

Cont c_1 = new Cont("411.1", "Client Alfa SRL");

Cont c_2 = new Cont("371", "Marfuri");

// definire variabilă polimorfă

OperatiuneContabila op;

// prima iniţializare variabilă polimorfă

op = new OperatiuneContabila(1,new Date());

op.addInregistrareContabila(

new InregistrareDebit(1, c_1, 100.0));

op.addInregistrareContabila(

new InregistrareCredit(2, c_2, 100.0));

// invocare prima instanţă OperatiuneContabila

// prin referinţa stocată în var. polimorfă

System.out.println("i.ID op: " + op.getIdOperatiune());

// a doua iniţializare variabilă polimorfă

op = new Vinzare(2, new Date(),"Alfa SRL");

op.addInregistrareContabila(

new InregistrareDebit(3, c_1, 50.0));

op.addInregistrareContabila(

new InregistrareCredit(4, c_2, 50.0));

// invocarea celei de a doua instanţe OperatiuneContabila

// prin referinţa stocată în var. polimorfă

System.out.println("ii.ID op: " + op.getIdOperatiune());

}

}

polimorfismul mesajelor (sau adresabilităţii), care are în

vedere combinarea domeniilor celor două forme de polimorfism anterior: folosind aceeași variabilă sunt gestionate două obiecte diferite (două instanțe provenind din clase diferite) și se invocă același nume de operație, dar sunt executate două metode de implementare diferite, provenind din clase diferite.

// Listing 3.10: Variabilă prin care sunt invocate operaţii cu

// acelaşi nume către instanţe din clase diferite

//----------------------------------------------------------------

Page 25: Capitolul 3 POO

Polimorfism și genericitate

25

public class OperatiuneContabila

implements Comparable<OperatiuneContabila>,

Validatable, IOperatieContabila{

... ... ...

public void addInregistrareContabila(

InregistrareContabila inregistrare){ ... }

... ... ...

}

//----------------------------------------------------------------

public abstract class OperatiuneComerciala

extends OperatiuneContabila{

... ... ...

}

//----------------------------------------------------------------

// Două implementări diferite pentru

// operaţie polimorformă addInregistrareContabila

//----------------------------------------------------------------

public class Vinzare extends OperatiuneComerciala{

... ... ...

public void addInregistrareContabila(

InregistrareContabila inregistrare){

System.out.println("Apel metoda addInregistrareContabila"+

"din clasa Vinzare");

if (!inregistrare.getCont().getCod().startsWith("411"))

throw new AppException("Inregistrare incompatibila"

+ "cu operatiune vinzare !");

super.addInregistrareContabila(inregistrare);

}

... ... ...

}

//----------------------------------------------------------------

public class Cumparare extends OperatiuneComerciala{

... ... ...

public void addInregistrareContabila(

InregistrareContabila inregistrare){

System.out.println("Apel metoda addInregistrareContabila"

+ "din clasa Cumparare");

if (inregistrare instanceof InregistrareDebit

&& !inregistrare.getCont()

.getCod().startsWith("401"))

throw new AppException("Inregistrare incompatibila"

+ " cu operatiune vinzare !");

super.addInregistrareContabila(inregistrare);

}

Page 26: Capitolul 3 POO

Capitolul 3

26

... ... ...

}

//------------------------------------------------------------

// Test invocare implementări operaţii abstracte

//------------------------------------------------------------ public class TestPolimorfismMesaje {

public static void main(String[] args){

Cont c_1 = new Cont("411.1", "Client Alfa SRL");

Cont c_2 = new Cont("401.1", "Furnizor Beta SRL");

// definire variabilă polimorfă

OperatiuneContabila op;

// prima iniţializare variabilă polimorfă

op = new Cumparare(1,new Date());

op.addInregistrareContabila(

new InregistrareDebit(1, c_2, 100.0));

// invocare prima instanţă OperatiuneContabila

//prin referinţa stocată în var. polimorfă

System.out.println("i. cont op 1: "

+ op.getInregistrari().get(0).getCont().getCod());

// a doua iniţializare variabilă polimorfă

op = new Vinzare(2, new Date(),"Alfa SRL");

op.addInregistrareContabila(

new InregistrareDebit(3, c_1, 50.0));

// invocarea celei de a doua instanţe OperatiuneContabila

// prin referinţa stocată în var. polimorfă

System.out.println("ii. cont op 2: "

+ op.getInregistrari().get(0).getCont().getCod());

}

}

-------------- Rezultate test: -----------------------------------

Apel metoda addInregistrareContabila din clasa Cumparare

i. cont op 1: 401.1

Apel metoda addInregistrareContabila din clasa Vinzare

ii. cont op 2: 411.1

------------------------------------------------------------------

3.4 Egalitatea şi comparabilitatea obiectelor Paragraful de față, rămânând tot în contextul discuţiei despre

polimorfism, aduce în discuție subiectul egalității obiectelor și polimorfismul operaţiei equals(), dar abordează şi comparabilitatea

Page 27: Capitolul 3 POO

Polimorfism și genericitate

27

obiectelor în sensul ordonării, polimorfismul operaţiilor compareTo() sau compare() din interfeţele Comparable sau Comparator.

Prin urmare, în continuare, vom încerca să răspundem la întrebările: Când sunt egale obiectele și ce semnificație poate avea această

egalitate? Când un obiect poate fi considerat mai mic sau anterior, ori

mai mare sau posterior, într-o ordine dată?

3.4.1 Egalitatea şi identitatea obiectelor. Operaţia equals din superclasa Object

În limbajul Java, operatorul „==” verifică de fapt conţinutul concret al

variabilelor care, în realitate, stochează referinţele obiectelor instanţiate din clase.

Verificarea obiectelor (la care se poate face referire prin variabile) în sensul identităţii presupune verificarea stării interne a acestora sau cel puţin a atributelor care pot diferenţia întotdeauna obiectele între ele.

În Java, în vârful tuturor ierarhiilor de moştenire, se găseşte clasa Object de la care toate clasele vor moşteni metoda equals() ce va putea fi supra-scrisă în sensul codificării identităţii drept principiu de egalitate

Figura 3.1 Egalitatea obiectelor

Page 28: Capitolul 3 POO

Capitolul 3

28

În listingul următor este codificat un exemplu privind utilizarea

metodei equals pentru implementarea identității semantice.

// Listing 3.11: Implementare identitate semantică

// prin metoda equals suprascrisă

//----------------------------------------------------------------

// Identitatea conturilor se bazează pe atributul cod

//----------------------------------------------------------------

public class Cont{

... ... ...

@Override

public boolean equals(Object obj) {

if (obj == null) {

return false;

}

if (getClass() != obj.getClass()) {

return false;

}

final Cont other = (Cont) obj;

if (

(this.cod == null)?

(other.cod != null) :

!this.cod.equals(other.cod)) {

return false;

}

return true;

}

... ... ...

}

//----------------------------------------------------------------

//Identitatea operaţiunilor se bazează pe atributul idOperatiune

//----------------------------------------------------------------

public class OperatiuneContabila{

... ... ...

@Override

public boolean equals(Object obj) {

if (obj == null) {

return false;

}

if (getClass() != obj.getClass()) {

return false;

}

final OperatiuneContabila other =

Page 29: Capitolul 3 POO

Polimorfism și genericitate

29

(OperatiuneContabila) obj;

if (this.idOperatiune != other.idOperatiune

&& (this.idOperatiune == null

|| !this.idOperatiune.equals(other.idOperatiune))) {

return false;

}

return true;

}

... ... ...

}

//----------------------------------------------------------------

// Identitatea înregistrărilor se bazează pe atributul id

//----------------------------------------------------------------

public class InregistrareContabila{

... ... ...

@Override

public boolean equals(Object obj) {

if (obj == null) {

return false;

}

if (getClass() != obj.getClass()) {

return false;

}

final InregistrareContabila other =

(InregistrareContabila) obj;

if (this.id != other.id && (this.id == null ||

!this.id.equals(other.id))) {

return false;

}

return true;

}

... ... ...

}

3.4.2 Obiecte comparabile şi obiecte comparatori. Interfeţele Comparable şi Comparator

Asigurarea compatibilității obiectelor cu un criteriu de ordonare se

poate face prin forțarea claselor să implementeze interfaţa Comparable. În acest fel vor fi obligate să asigure o implementare corespunzătoare operaţiei compareTo(Object other) prin care pot fi ordonate instanţele acestora. Valoarea returnată a acestei operații având următoarea semnificație:

Page 30: Capitolul 3 POO

Capitolul 3

30

număr negativ – mai mic; 0 – egal; număr pozitiv – mai mare. Dacă nu există posibilitatea forțării claselor ale căror obiecte urmează

să fie ordonate să implementeze interfața Comparable, o cale alternativă constă la apelarea unui intermediar: o clasă suplimentară care să implementeze interfaţa Comparator. Această clasă va furniza un serviciu (sau un operator) de ordonare pentru instanţele altor clase prin operaţiunea compare(Object firstObject,Object secondObject) a cărei valoare returnată are următoarea semnificație: număr negativ înseamnă că primul obiect (desemnat prin primul

parametru) ar trebui să se găsească înaintea (este mai mic) celui (decât) de-al doilea;

0 înseamnă că cele două obiecte sunt egale; număr pozitiv înseamnă că primul obiect ar trebui să se găsească

în urma (este mai mare) celui (decât) de-al doilea.

// Listing 3.12: Implementare pentru conceptul generic

// de comparabilitate

//----------------------------------------------------------------

// Identitatea conturilor poate fi folosită drept

// criteriu de comparabilitate

//----------------------------------------------------------------

public class Cont implements Comparable<Cont>{

... ... ...

public int compareTo(Object obj) {

if (obj == null) {

throw new RuntimeException("Compare to null !!");

}

if (getClass() != obj.getClass()) {

throw new RuntimeException("Incomparable types !!");

}

final Cont other = (Cont) obj;

return this.getCod().compareTo(other.getCod());

}

... ... ...

}

//----------------------------------------------------------------

// Comparabilitatea operaţiunilor se poate baza

// pe atributul dataContabilizare

//----------------------------------------------------------------

public class OperatiuneContabila

implements Comparable< OperatiuneContabila >{

Page 31: Capitolul 3 POO

Polimorfism și genericitate

31

... ... ...

public int compareTo(OperatiuneContabila op) {

if (this.getDataContabilizare()

.after(op.getDataContabilizare()))

return 1;

if (this.getDataContabilizare()

.before(op.getDataContabilizare()))

return -1;

return 0;

}

... ... ...

}

//----------------------------------------------------------------

// Comparabilitatea înregistrărilor se poate baza

// pe atributul nrOrdine

//----------------------------------------------------------------

public class InregistrareContabila

implements Comparable< InregistrareContabila >{

... ... ...

public int compareTo(InregistrareContabila other) {

return this.nrOrdine.compareTo(other.getNrOrdine());

}

... ... ...

}

3.5 Obiecte în colecţii. Colecţii generice După ce am discutat în capitolul precedent despre anumite tipuri de

obiecte pe care le-am calificat drept fundamentale, în continuare vom dezbate acele structuri fără de care programarea orientată obiect nu ar putea avea sau nu ar putea implementa atributul multiplicităţii, decât folosind tablourile într-un mod limitat şi destul de inflexibil. Este vorba despre colecţii, simple sau generice, indexate natural sau prin chei specifice, cu şi fără duplicate, ordonate sau nu.

Structura bibliotecii (API) standard privind colecţiile din mediul Java este organizată pornind de la un sistem bazat pe următoarele interfeţe:

interfața Collection, pentru colecţiile simple, ale căror elemente

sunt accesibile prin intermediul unui obiect de tip Iterator, având ca specializări sub-interfeţele: List, pentru colecţii care permit duplicate şi ale căror elemente

sunt accesibile prin indecşi – valori întregi (int), asemănător tablourilor liniare (vectorilor);

Page 32: Capitolul 3 POO

Capitolul 3

32

Set, pentru colecţii care nu permit duplicate;

Map, pentru colecţii în care obiectele-valoare sunt stocate numai pe bază (sau în asociaţie) cu o cheie, ce poate fi conformă cu orice tip convențional.

Cele mai comune implementări concrete pentru aceste interfeţe,

disponibile implicit în mediul Java standard, constau în următoarele clase: ArrayList, LinkedList – pentru List; HashSet şi TreeSet – pentru Set; HashMap şi TreeMap – pentru Map.

Interfeţele standard pentru colecţiile Java convenționale au însă şi alte

implementări furnizate de alţi producători. Printre aceste implementări alternative cele mai cunoscute sunt Appache-Commons-Collections, şi Google-Collections.

3.5.1 API pentru colecţiile de obiecte în mediul Java. Colecţii indexate, sortate şi tipizate

Pentru a da un sens mai larg termenului „colecţii” unii autori folosesc

noţiunea de container în sensul de întreg format din mai multe părţi omogene sau neomogene în ceea ce priveşte tipologia lor.

Prima distincţie între aceste clase se face din punctul de vedere al

accesului, şi anume: pe de o parte există colecţii în care accesul la obiecte se face prin

index (asemănător array-urilor) sau secvenţial, parcurgând iterativ elementele (valorile). Din punctul de vedere al utilizatorului aceste colecţii sunt văzute ca fiind de tip Collection;

pe de altă parte există colecţii în care obiectelor le sunt asociate chei, o cheie putând face legătura cu un singur obiect. Evident, accesul se bazează pe aceste chei. Prin urmare, elementele unei astfel de colecţii sunt de fapt perechi de obiecte, dintre care unul serveşte drept cale de acces spre celălalt. Din punctul de vedere al utilizatorului, astfel de colecţii sunt văzute ca fiind de tip Map.

Page 33: Capitolul 3 POO

Polimorfism și genericitate

33

Collection Map

Set List

ArrayList LinkedList HashSet TreeSet

HashMap TreeMap

Iterator

ListIterator

Figura 3.2 O parte din clasele cele mai importante ale API-ului privind

colecţiile

Definiţia interfeţei Collection cuprinde, în esenţă, setul de operații

prezentat în tabelul următor.

Tabelul 3.1 Definiția operațiilor interfeței Collection

Operații ale

interfeței Collection

Explicații

public int size() Returnează numărul de elemente al

colecţiei.

public boolean isEmpty() Verifică dacă există cel puţin un

element iniţializat în colecţie.

public boolean containts(Object

element)

Verifică existenţa elementului

specificat în colecţie.

public void add(Object element) Adaugă un element în colecţie.

public void remove(Object

element)

Şterge un element din colecţie.

public Iterator iterator() Întoarce o instanţă Iterator prin ale

cărei metode next() şi hasNext() se

poate constitui o buclă iterativă

pentru parcurgerea colecţiei.

public boolean

addAll(Collection c)

Adaugă în colecţia curentă

elementele altei colecţii.

public boolean

removeAll(Collection c)

Şterge din colecţia curentă toate

elementele care se găsesc şi în

colecţia specificată (prin argument).

public boolean Şterge din colecţia curentă toate

Page 34: Capitolul 3 POO

Capitolul 3

34

retainAll(Collection c) elementele, cu excepţia celor care se

regăsesc în colecţia specificată.

public boolean

containsAll(Collection c)

Verifică dacă toate elementele din

colecţia specificată se găsesc şi în

colecţia curentă.

public void clear() Goleşte colecţia.

public Object[] toArray() Creează un array pe baza

elementelor colecţiei curente.

Prin urmare, indiferent de clasa care reprezintă materializarea fizică a

colecţiei, utilizatorii acesteia o pot accesa în orice situaţie folosind

metodele din definiţia interfeţei de mai sus.

Containerele de tip Colection sunt diferenţiate la rândul lor în două

categorii:

colecţii ordonate de elemente neduplicate, caz în care accesarea

acestora se face prin intermediul interfeţei Set;

colecţii ne-ordonate de elemente păstrate în ordinea adăugării,

interfaţa List fiind esenţială în acest caz.

Definiţia interfeţei List care, bineînţeles, extinde interfaţa Collection,

după cum rezultă şi din figura 3.2, cuprinde grupul de operații prezentat în

tabelul următor.

Tabelul 3.2 Definiția operațiilor interfeței List

Operații ale

interfeței List

Explicații

public boolean addAll(int,

colection)

Inserează toate elementele din

colecţia specificată (prin argument)

în colecţia iniţială începând la o

anumită poziţie.

public Object get(int) Returnează elementul de la poziţia

(indexul) specificat.

public Object set(int, Object) Înlocuieşte elementul de la poziţia

indicată cu obiectul specificat (prin

al doilea argument). Returnează

Page 35: Capitolul 3 POO

Polimorfism și genericitate

35

elementul care se găsea anterior pe

respectiva poziţie.

public Object remove(int) Îndepărtează din colecţie elementul

de la poziţia specificată. Returnează

elementul eliminat.

public int indexOf(Object) Returnează poziţia la care apare

prima dată în colecţie elementul

specificat sau –1 în cazul

insuccesului.

public int lastIndexOf(Object) Returnează poziţia la care apare

ultima dată în colecţie elementul

specificat sau –1 în cazul

insuccesului.

public ListIterator listIterator() Returnează o instanţă ListIterator

(subclasă a Iterator) pentru

parcurgerea elementelor din listă.

public ListIterator

listIterator(int)

Returnează o instanţă ListIterator

(subclasă a Iterator) pentru

parcurgerea elementelor din listă

începând cu poziţia specificată.

public List subList(int from, int

to)

Returnează o nouă colecţie de tip

List ale cărei elemente sunt preluate

din colecţia iniţială între poziţiile

specificate.

Faţă de interfaţa Collection, List adaugă, în special, metode destinate

accesului la elementele individuale, ţinând seama de faptul că indexul

fiecăruia este asociat consecutiv funcţie de adăugarea în listă. Parcurgerea

elementelor se poate face clasic, folosind indexul, sau poate fi realizată

într-o altă ordine dictată de o instanţă ListIterator.

În general, un obiect de tip Iterator trebuie să implementeze

următoarele metode:

public boolean hasNext();

public Object next();

public void remove();

Un ListIterator extinde interfaţa Iterator şi este asociat exclusiv List-

elor. El adăugă metode opţionale prin care poate gestiona elementele unei

liste, add(), set(), remove(), precum şi modalităţi de parcurgere

Page 36: Capitolul 3 POO

Capitolul 3

36

bidirecţională a acesteia, hasPrevious(), previous(), nextIndex(),

previousIndex().

Biblioteca privind colecţiile, plasată în pachetul java.util, oferă două

clase concrete pentru implementarea listelor:

LinkedList care asigură un acces secvenţial bine optimizat, o

implementare eficientă a operaţiilor de inserare şi ştergere din

interiorul listei. Accesul aleatoriu la elementele listei este însă

relativ ineficient din punctul de vedere al timpului.

ArrayList care se bazează pe un Array şi permite un acces

aleatoriu rapid la oricare element din colecţie. Inserarea şi

eliminarea elementelor din „mijlocul” listei este însă costisitoare.

Am menționat deja că un Set este o colecţie care nu prezintă elemente

duplicate. Ca urmare, interfaţa Set nu prezintă elemente deosebite faţă de

interfaţa Collection, cu excepţia caracteristicii amintită anterior. Prin

urmare, dubla adăugare, prin apelul operației add(Object), a unui element

într-un set nu va avea ca rezultat duplicarea referinței acestuia, al doilea

apel add(Object) neavând nici un efect.

Distribuţia standard Java oferă două căi de obţinere a unui Set,

folosind următoarele clase concrete:

HashSet care este utilă pentru seturile în care timpul de căutare

este esenţial, obiectele care formează elementele unui astfel de set

trebuie să definească corespunzător metoda hashCode();

TreeSet care este util în obținerea de un seturi ordonate organizate

pe structuri de tip arborescent, aşa încât elementele pot fi obţinute

într-o secvenţă ordonată.

Pe lângă Collection, am menționat și un alt mod de organizare a

obiectelor în colecții descrise prin prin interfaţa Map. O astfel de

structură de date se bazează pe configurarea elementelor ca perechi cheie-

valoare. Fiecare cheie are asociată cel mult o singură valoare.

Page 37: Capitolul 3 POO

Polimorfism și genericitate

37

Tabelul 3.2 Definiția operațiilor interfeței Map

Operații ale

interfeței Map

Explicații

public Set keySet() Returnează cheile ca un set.

public Collection values() Returnează valorile ca o colecţie

(Collection).

public Set entrySet() Returnează elementele cheie-valoare

(instanţele java.util.Map.Entry) ca un

Set obişnuit.

public boolean

containsKey(Object)

Verifică existenţa unei chei.

public boolean

containsValue(Object)

Verifică existenţa unei valori.

public Object get(Object key) Returnează o valoare cunoscându-i

cheia asociată.

public Object put(Object key,

Object value)

Asociază valoarea specificată cu cheia

precizată prin primul argument.

Returnează valoarea asociată anterior cu

respectiva cheie.

public void putAll(Map) Copiază toate mapările din containerul

Map specificat în cel curent.

public Object remove(Object

key)

Elimină valoarea asociată cu cheia

specificată prin argument. Returnează

elementul (valoarea) eliminată.

public boolean isEmpty() Verifică dacă există asocieri cheie-

valoare.

public int size() Numărul de asocieri chei-valoare

existente

public void clear() Șterge toate elementele-asocieri din

Map.

Fiecare element al acestui tip de colecţie sau fiecare pereche cheie-

valoare sau fiecare intrare reprezintă un obiect al cărui tip este definit prin

interfaţa java.util.Map.Entry:

public Object getKey();

public Object getValue();

public Object setValue(Object);

public boolean equals(Object);

public int hashCode();

Page 38: Capitolul 3 POO

Capitolul 3

38

După cum se observă din tabelul de mai sus, un container de tip Map

poate fi văzut din trei perspective, toate trei fiind de tip Collection: ca un

Set de chei, ca o Collection de valori, ca un Set de asociaţii cheie-valoare.

Prin urmare, parcurgerea unui Map se va realiza funcţie de iteratorul

colecţie sub care este văzut, printr-o structură de genul celei prezentate în

listingul următor.

// Listing 3.13: Obținerea unei interator pentru

// parcurgerea elementelor unui Map

Set s = map.entrySet();

Iterator i = s.Iterator();

while(i.hasNext(){ i.next; ...}

Distribuţia Java oferă pentru acest tip de container două implementări

concrete prin clasele:

HashMap, care se bazează pe o tabelă de tip hash şi furnizează o

performanţă uniformă pentru inserarea şi localizarea perechilor de

valori;

TreeMap, care se bazează pe o structură arborescentă, astfel încât

cele trei perspective ale containerului vor putea fi obţinute deja în

mod ordonat.

3.5.2 Colecţii tipizate şi structuri de control pentru parcurgerea colecţiilor

Sunt uşor sesizabile avantajele colecţiilor asupra tablourilor simple, o

mai simplă gestiune a accesului la elemente, o parcurgere mai facilă prin

iteratori, o mai simplă gestiune a capacităţii de memorare a elementelor,

posibilitatea schimbării modului de referenţiere a elementelor prin chei de

altă natură decât valorile Integer. Poate fi, însă, evidenţiat şi un major

dezavantaj faţă de tablourile native: netipizarea, adică elementele stocate

sunt retrogradate la stadiul de obiecte simple, astfel că re-tratarea lor ca

obiecte ale unor tipuri cunoscute implică nenumărate operaţii de casting.

Page 39: Capitolul 3 POO

Polimorfism și genericitate

39

Caracteristicile de genericitate introduse odată cu Java 5 elimină acest

dezavantaj, astfel că declararea colecţiilor poate fi acum însoţită şi de

declaraţia tipului concret al obiectelor stocate, specificat între paranteze

unghiulare, după cum se poate observa și în exemplul din listingul

următor.

// Listing 3.14: Obținerea unui iterator generic

List<Client> clienţi ...

Iterator<Client> i = clienti.iterator();

Parcurgerea unei Colecţii

După cum am văzut deja, un Iterator reprezintă o interfaţă care

specifică un mecanism de parcurgere a unei colecţii prin metodele:

boolean hasNext() ;

Object next() .

De asemenea, ListIterator extinde interfaţa Iterator şi este asociat

exclusiv List-elor, metodele adiționale fiind: add(), set(), remove(),

hasPrevious(), previous() ş.a.

Parcurgerea „clasică” a colecţiilor folosind un astfel de obiect ar

trebui să se realizeze după cum se prezintă în listingul următor.

// Listing 3.15: Parcurgerea unei colecții cu iterator și casting

Iterator i = colectieConturi.iterator();

Cont c;

while( i.hasNext() ){

c = (Cont)i.next(); // necesita operatie de casting

// proceseaza c

}

Parcurgerea colecţiilor tipizate (sau generică) elimină necesitatea

operaţiei de casting, după cum se poate observa în exemplul următor.

// Listing 3.16: Parcurgerea unei colecții cu iterator

// fără casting

Iterator<Cont> i = colectieConturi.iterator();

Page 40: Capitolul 3 POO

Capitolul 3

40

Cont c;

while( i.hasNext() ){

c = i.next(); // nu necesita operatie de casting

// proceseaza c

}

De asemenea, pentru colecţiile generice sau tipizate a fost introdusă

chiar şi o structură de control repetitivă de parcurgere a colecţiilor

specifică.

// Listing 3.17: Parcurgerea unei colecții fără iterator

for(Client c: Client){

// procesează c

}

// în loc de

Iterator<Client> i = clienti.iterator();

Client c;

while( i.hasNext()){

c = i.next();

// procează c

}

Importanța egalității obiectelor gestionate în colecţii

Polimorfismul metodei equals este esenţial în cazul colecţiilor.

în cazul colecțiilor de tip Collection, metoda equals este invocată

indirect elementelor individuale în mecanismele interne ale

claselor concrete de implementare pentru confirmarea existenţei

unui obiect prin apelul operației contains(Object o);

în cazul colecțiilor specializate de tip List, metoda equals este

invocată în cautarea (indexului) unui obiect prin operația

indexOf(Object o);

în cazul colecțiilor de tip Map, metoda equals este invocată în

căutarea unei chei prin operaţia get(Object key) sau la

Page 41: Capitolul 3 POO

Polimorfism și genericitate

41

confirmarea existenţei unei valori prin operația

containsValue(Object o).

3.5.3 Elemente specifice claselor de implementare a interfeţelor standard privind colecţiile

Până acum am discutat şi prezentat câteva secvenţe de cod privind

utilizarea API-ului standard privind colecţiile, mai exact a interfeţelor

care definesc această funcţionalitate. În continuare vom prezenta câteva

aspecte privind clasele care de fapt realizează serviciile (sau operaţiile)

specificate de aceste interfeţe.

Clasa ArrayList

Clasa ArrayList implementează, după cum am văzut anterior, interfaţa

List la care vor adăuga, bineînţeles, constructorii necesari obţinerii

concrete a unei astfel de colecţii. Definiţia generică (în sensul tipizării)

acestei clase este următoarea:

public ArrayList<T>();

public ArrayList<T>(int size);

public ArrayList<T>(Collection c);

public void trimToSize();

public void ensureCapacity(int minSize);

public Object clone();

Prin urmare, o astfel de „listă” poate fi obţinută specificându-i, însă nu

obligatoriu, dimensiunea iniţială (numărul iniţial de poziţii rezervate),

spre deosebire de cazul array-urilor unde era absolut necesară

cunoaşterea numărului de elemente al colecţiei. De asemenea, un

ArrayList se poate obţine şi pe baza unei colecţii deja existente. Dacă

dimensiunea iniţial rezervată a fost prea mare, existând un număr de

„rubrici” goale, elementele nefolosite pot fi eliminate prin trimToSize(),

Page 42: Capitolul 3 POO

Capitolul 3

42

iar dacă iniţial nu a fost specificată capacitatea listei sau cea iniţială

urmează a fi depăşită, se poate asigura rezervarea unui număr de elemente

prin metoda ensureCapacity().

În listingul de mai jos este prezentat un exemplu de declarare,

populare şi acces al unei colecţii ArrayList.

// Listing 3.18: Test colecţii instanţiate din ArrayList

public class TestArrayList {

public static void main(String[] args) {

List<String> list = new ArrayList<String>(2);

list.add("primul");

list.add("al doilea");

list.add("al treilea");

// s-a depasit deja capacitatea initiala, asa ca

// lista va final obligata sa-si redefineasca

// automat dimensiunea

list.add(3, "al patrulea");

// operatie validata fiindca urmeaza indexul 3

// list.add(5, "primul");

// operatie invalidata fiindca urmeaza indexul 4

list.add(4, "al cincilea");

list.add(2, "ultimul");

// Se parcurge lista in stil „clasic":

System.out.println("Iteratie clasica folosind for()");

for(int i=0; i<list.size(); i++)

System.out.println(list.get(i));

// Se parcurge lista prin interator in ordinea indexarii:

System.out.println(

"Iteratie cu iterator in ordinea indexarii");

// Se Obtine un iterator care porneste de la primul index

Iterator<String> iterator = list.listIterator();

while(iterator.hasNext())

System.out.println(iterator.next());

// sau

for(String e: list)

System.out.println(e);

Page 43: Capitolul 3 POO

Polimorfism și genericitate

43

// Parcurgem lista prin interator in ordine inversa:

System.out.println("Iteratie cu iterator in "

+ "ordinea inversa indexarii");

// Reconsider iteratorul anterior ca ListIterator, si care

// in iteratia de mai jos va porni de la ultimul element,

// la care a ajuns prin iteratia de msi sus

ListIterator<String> listIterator =

(ListIterator<String>)iterator;

while(listIterator.hasPrevious())

System.out.println(listIterator.previous());

}

}

Rezultatul va fi următorul:

Iteratie clasica folosind for()

primul

al doilea

ultimul

al treilea

al patrulea

al cincilea

Iteratie cu iterator in ordinea indexarii

primul

al doilea

ultimul

al treilea

al patrulea

al cincilea

Iteratie cu iterator in ordinea inversa indexarii

al cincilea

al patrulea

al treilea

ultimul

al doilea

primul

Clasa HashMap

Clasa HashMap implementează, după cum am arătat anterior, interfaţa

Map, oferind avantajul asocierii elementelor cu orice fel de tip de obiecte-

chei, nelimitându-se doar la valorile primitive întregi (int) specifice

Page 44: Capitolul 3 POO

Capitolul 3

44

indecşilor celorlalte tipuri de colecţii (Array-tablou clasic sau List). De

asemenea, accesul obiectelor-valori folosind obiectele-chei se realizează

la performanţe rezonabile, ceea ce face din acest tip de container unul

dintre cele mai folositoare.

În definiţia clasei HashMap se găsesc, pe lângă metodele

implementate din interfaţa Map, în primul rând metodele constructor

pentru obţinerea concretă a unei astfel de structuri.

public HashMap<T,T>();

public HashMap<T,T> (int size, float load);

public HashMap<T,T> (int size);

public HashMap<T,T> (Map<T,T>);

Parametrul size din constructorii de mai sus semnifică capacitatea

iniţială a tabelei, iar parametrul load specifică dimensiunea la care trebuie

să ajungă „mapa” pentru a fi relocalizată în altă zonă de memorie.

În listingul de mai jos este prezentat un exemplu concret de folosire a

clasei HashMap.

// Listing 3.19: Test HashMap

import java.util.Collection;

import java.util.HashMap;

import java.util.Iterator;

import java.util.Map;

import java.util.Map.Entry;

import java.util.Set;

public class TestHashMapConturi {

public static void main(String[] args){

String[] coduri = {"301", "401", "101", "212", "531"};

Cont[] conturi = {

new Cont("301", "Materii prime"),

new Cont("401", "Furnizori"),

new Cont("101", "Capital"),

new Cont("212", "Constructii"),

new Cont("531", "Casa")

};

Map<String, Cont> mapConturi =

new HashMap<String, Cont>();

// populez indicative

Page 45: Capitolul 3 POO

Polimorfism și genericitate

45

for (int i=0; i<coduri.length; i++)

mapConturi.put(coduri[i], conturi[i]);

// parcurg cele trei perspective ale containerului

// mai intai cheile

Set<String> chei = mapConturi.keySet();

Iterator<String> iteratorChei = chei.iterator();

System.out.println("Cheile:");

while(iteratorChei.hasNext())

System.out.println(iteratorChei.next());

// apoi valorile

Collection<Cont> valori = mapConturi.values();

Iterator<Cont> iteratorValori = valori.iterator();

System.out.println("Valorile:");

while(iteratorValori.hasNext())

System.out.println(iteratorValori.next());

// sau

for(Cont v: valori)

System.out.println(v);

// in fine setul de perechi chei-valoare

Set<Entry<String, Cont>> intrari = mapConturi.entrySet();

Iterator<Entry<String, Cont>> iteratorIntrari =

intrari.iterator();

System.out.println("Perechile cheie-valoare:");

while(iteratorIntrari.hasNext()){

java.util.Map.Entry<String, Cont> intrare =

iteratorIntrari.next();

System.out.println(intrare.getKey() + " -- "

+ intrare.getValue());

}

// sau

for(java.util.Map.Entry<String, Cont> intrare: intrari)

System.out.println(intrare.getKey() + "-"

+ intrare.getValue());

// extrag o valoare cunoscindu-i cheia

String id = "401";

System.out.println("Codul " + id +

" corespunde contului " + mapConturi.get(id));

}

}

Rezultatul va fi următorul:

Page 46: Capitolul 3 POO

Capitolul 3

46

Cheile:

531

212

301

401

101

Valorile:

Cont 531 - Casa

Cont 212 - Constructii

Cont 301 - Materii prime

Cont 401 - Furnizori

Cont 101 - Capital

Cont 531 - Casa

Cont 212 - Constructii

Cont 301 - Materii prime

Cont 401 - Furnizori

Cont 101 – Capital

Perechile cheie-valoare:

531 -- Cont 531 - Casa

212 -- Cont 212 - Constructii

301 -- Cont 301 - Materii prime

401 -- Cont 401 - Furnizori

101 -- Cont 101 – Capital

531-Cont 531 - Casa

212-Cont 212 - Constructii

301-Cont 301 - Materii prime

401-Cont 401 - Furnizori

101-Cont 101 – Capital

Codul 401 corespunde contului Cont 401 - Furnizori

Colecţii sortate

Aranjarea elementelor în colecţii se poate dovedi de foarte multe ori

utilă aplicaţiilor care îşi gestionează obiectele cu ajutorul acestora.

Ordonarea elementelor în colecţii, folosind criterii specifice, se poate

realiza în două feluri:

Page 47: Capitolul 3 POO

Polimorfism și genericitate

47

extrinsec, adică după ce elementele au fost adăugate în colecţii,

ele vor fi rearanjate folosind o clasă utilitară,

java.util.Collections, furnizată tot de biblioteca privind colecţiile.

Criteriul de sortare este furnizat sub forma unui obiect de tip

Comparator (vezi exemplul următor);

// Listing 3.20: Test ordonare colecţii

public class TestOrdonareListOperatiuni {

public static void main(String[] args) throws ParseException{

SimpleDateFormat format =

new SimpleDateFormat("dd/MM/yyyy");

List<OperatiuneContabila> operatiuni =

new ArrayList<OperatiuneContabila>();

operatiuni.add(new OperatiuneContabila(1,

format.parse("01/06/2009")));

operatiuni.add(new OperatiuneContabila(2,

format.parse("01/03/2009")));

operatiuni.add(new OperatiuneContabila(3,

format.parse("01/02/2009")));

operatiuni.add(new OperatiuneContabila(4,

format.parse("01/04/2009")));

Collections.sort(operatiuni,

new ComparatorOperatiuniDupaData());

System.out.println("Ordonare operatiuni dupa data :");

for (OperatiuneContabila o : operatiuni)

System.out.println(o.getIdOperatiune() + " -- " +

format.format(o.getDataContabilizare()));

Collections.sort(operatiuni,

new ComparatorOperatiuniDupaId());

System.out.println("Ordonare operatiuni dupa id :");

for (OperatiuneContabila o : operatiuni)

System.out.println(o.getIdOperatiune() + " -- " +

format.format(o.getDataContabilizare()));

}

static class ComparatorOperatiuniDupaData

implements Comparator<OperatiuneContabila>{

public int compare(OperatiuneContabila o1,

OperatiuneContabila o2) {

Page 48: Capitolul 3 POO

Capitolul 3

48

return o1.getDataContabilizare()

.compareTo(o2.getDataContabilizare());

}

}

static class ComparatorOperatiuniDupaId

implements Comparator<OperatiuneContabila>{

public int compare(OperatiuneContabila o1,

OperatiuneContabila o2) {

return o1.getIdOperatiune()

.compareTo(o2.getIdOperatiune());

}

}

}

Rezultat:

Ordonare operatiuni dupa data :

3 -- 01/02/2009

2 -- 01/03/2009

4 -- 01/04/2009

1 -- 01/06/2009

Ordonare operatiuni dupa id :

1 -- 01/06/2009

2 -- 01/03/2009

3 -- 01/02/2009

4 -- 01/04/2009

intrinsec, adică elementele sunt aranjate corespunzător pe măsură

ce sunt adăugate în colecţii de tip TreeSet sau TreeMap (în cazul

mapelor, de fapt cheile sunt sortate). Criteriul de selecţie este

dedus din modul de implementare a interfeţei Comparable aplicată

clasei din care sunt instanţiate obiectele sau cheile, ce urmează a fi

ordonate (vezi exemplul următor).

// Listing 3.21: Test ordonare în TreeSet

public class OperatiuneContabila implements

Comparable<OperatiuneContabila>{

... ... ...

Page 49: Capitolul 3 POO

Polimorfism și genericitate

49

public int compareTo(OperatiuneContabila o) {

OperatiuneContabila op = (OperatiuneContabila) o;

if (this.getDataContabilizare()

.after(op.getDataContabilizare()))

return 1;

if (this.getDataContabilizare()

.before(op.getDataContabilizare()))

return -1;

return 0;

}

... ... ...

}

public class TestTreeSetOperatiuni {

public static void main(String[] args) throws ParseException {

SimpleDateFormat format =

new SimpleDateFormat("dd/MM/yyyy");

Collection<OperatiuneContabila> operatiuni =

new TreeSet<OperatiuneContabila>();

operatiuni.add(new OperatiuneContabila(1,

format.parse("01/06/2009")));

operatiuni.add(new OperatiuneContabila(2,

format.parse("01/03/2009")));

operatiuni.add(new OperatiuneContabila(3,

format.parse("01/02/2009")));

operatiuni.add(new OperatiuneContabila(4,

format.parse("01/04/2009")));

System.out.println("Ordonare operatiuni dupa data :");

for (OperatiuneContabila o : operatiuni) {

System.out.println(o.getIdOperatiune() + " -- " +

format.format(o.getDataContabilizare()));

}

}

}

Rezultat:

Ordonare operatiuni dupa data :

3 -- 01/02/2009

2 -- 01/03/2009

Page 50: Capitolul 3 POO

Capitolul 3

50

4 -- 01/04/2009

1 -- 01/06/2009

Dacă în exemplul anterior s-a demonstrat cum se poate ordona o colecție

simplă, în exemplul următor se urmărește ordonarea unei colecții-map de

corespondențe.

// Listing 3.22: Test ordonare chei în TreeMap

public class TestTreeMapConturi {

public static void main(String[] args){

Cont[] conturi = {

new Cont("301", "Materii prime"),

new Cont("401", "Furnizori"),

new Cont("101", "Capital"),

new Cont("212", "Constructii"),

new Cont("531", "Casa")

};

Map<String, Cont> mapConturiOrdonateDupaCod =

new TreeMap<String, Cont>();

for (Cont c : conturi){

mapConturiOrdonateDupaCod.put(c.getCod(), c);

}

// Ordonare conturi dupa cod

System.out.println("Ordonare conturi dupa cod: ");

for (String cod : mapConturiOrdonateDupaCod.keySet()){

System.out.println(mapConturiOrdonateDupaCod.get(cod));

}

Map<String, Cont> mapConturiOrdonateDupaDenumire =

new TreeMap<String, Cont>();

for (Cont c : conturi){

mapConturiOrdonateDupaDenumire

.put(c.getDenumire(), c);

}

// Ordonare conturi dupa denumire

System.out.println("Ordonare conturi dupa denumire: ");

for (String denumire :

Page 51: Capitolul 3 POO

Polimorfism și genericitate

51

mapConturiOrdonateDupaDenumire.keySet()){

System.out.println(mapConturiOrdonateDupaDenumire

.get(denumire));

}

}

}

Rezultat:

Ordonare conturi dupa cod:

Cont 101 - Capital

Cont 212 - Constructii

Cont 301 - Materii prime

Cont 401 - Furnizori

Cont 531 - Casa

Ordonare conturi dupa denumire:

Cont 101 - Capital

Cont 531 - Casa

Cont 212 - Constructii

Cont 401 - Furnizori

Cont 301 - Materii prime

Page 52: Capitolul 3 POO