Scrigroup - Documente si articole

     

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


Derivare, mostenire, polimorfism - Clase derivate

java



+ Font mai mare | - Font mai mic



Derivare, mostenire, polimorfism

Clase derivate



Derivarea inseamna definirea unei clase D ca o subclasa a unei clase A, de la care "mosteneste" toate variabilele si metodele publice. Subclasa D poate redefini metode mostenite de la clasa parinte (superclasa) A si poate adauga noi metode (si variabile) clasei A. Tipul D este un subtip al tipului A. La definirea unei clase derivate se foloseste cuvantul cheie extends urmat de numele clasei de baza. Exemplu:

// vector ordonat cu inserare element nou

public class SortedVector extends Vector

// metoda redefinita: cautare binara in vector ordonat

public int indexOf (Object obj)

}

In Java se spune ca o subclasa extinde functionalitatea superclasei, in sensul ca ea poate contine metode si date suplimentare. In general o subclasa este o specializare, o particularizare a superclasei si nu extinde domeniul de utilizare al superclasei.

Cuvantul super, ca nume de functie, poate fi folosit numai intr-un constructor si trebuie sa fie prima instructiune (executabila) din constructorul subclasei. Exemplu:

public class IOException extends Exception

public IOException(String s)

}

Principala modalitate de specializare a unei clase este redefinirea unor metode din superclasa. Prin redefinire ('override') se modifica operatiile dintr-o metoda, dar nu si modul de utilizare al metodei.

De multe ori, subclasele nu fac altceva decat sa redefineasca una sau cateva metode ale superclasei din care sunt derivate. Redefinirea unei metode trebuie sa pastreze 'amprenta' functiei, deci numele si tipul metodei, numarul si tipul argumentelor .

Majoritatea claselor Java care contin date redefinesc metodele "equals" si "toString" (mostenite de la clasa Object), pentru a permite compararea de obiecte si conversia datelor din clasa intr-un sir de caractere. Nu se poate extinde o clasa finala (cu atributul final) si nu pot fi redefinite metodele din superclasa care au unul din modificatorii final, static, private.

O metoda redefinita intr-o subclasa 'ascunde' metoda corespunzatoare din superclasa, iar o variabila dintr-o subclasa poate 'ascunde' o variabila cu acelasi nume din superclasa. Apelarea metodei originale din superclasa nu este in general necesara pentru un obiect de tipul subclasei, dar se poate face folosind cuvantul cheie 'super' in locul numelui superclasei. Exemplu:

// vector ordonat cu adaugare la sfarsit si reordonare

public class SortedVector extends Vector

// metoda mostenita automat dar interzisa in subclasa

public void insertElementAt (Object obj, int pos)

}

Daca metoda din subclasa nu are exact acelasi prototip (nume,tip,argumente) cu metoda din superclasa atunci nu are loc o redefinire si doar se adauga alta metoda la cele mostenite. Pentru a permite compilatorului sa verifice ca are loc o redefinire de metoda mostenita s-a introdus din 1.5 adnotarea @Override ca prefix al metodei:

import java.util.* ;

// vector ordonat cu adaugare la sfirsit si reordonare

class SortVec extends Vector

public SortVec ( Vector v)

@Override

public void addElement ( String obj)

Lipsa adnotarii @Override in exemplul anterior nu permite detectarea erorii ca metoda "addElement" cu argument String nu redefineste metoda "addElement" din superclasa, cu argument Object. O subclasa se poate referi la variabilele superclasei fie direct prin numele lor, daca variabilele din superclasa sunt de tip public sau protected, fie indirect, prin metode publice ale superclasei. Exemplu:

public class Stack extends Vector

}

Exemplul urmator arata cum putem scrie o metoda recursiva intr-o clasa derivata:

class FileExt extends File

// afisare recursiva fisiere din subdirectoare

// varianta 1: cu metoda listFiles

public void printFiles (String sp)

}

// varianta 2: cu metoda list

public void printFiles (String sp)

}

}

Derivarea ca metoda de reutilizare

Derivarea este motivata uneori de necesitatea unor clase care folosesc in comun anumite functii dar au si operatii specifice, diferite in clasele inrudite. O solutie de preluare a unor functii de la o clasa la alta este derivarea.

O clasa derivata 'mosteneste' de la superclasa sa datele si metodele nestatice cu atributele public si protected . Clasa "SortedVector", definita anterior, mosteneste metodele clasei Vector: elementAt, indexOf, toString s.a. Exemplu:

SortedVector a = new SortedVector();

System.out.println ( a.toString()); // afisare continut vector

Chiar si metoda "addElement" redefinita in clasa derivata, refoloseste extinderea automata a vectorului, prin apelul metodei cu acelasi nume din superclasa.

In Java atributele membrilor mosteniti nu pot fi modificate in clasa derivata: o variabila public din superclasa nu poate fi facuta private in subclasa.

Metodele si variabilele mostenite ca atare de la superclasa nu mai trebuie declarate in subclasa si pot fi folosite fara prefixul super. In schimb, trebuie definite functiile constructor, metodele si datele suplimentare (daca exista) si trebuie redefinite metodele care se modifica in subclasa.

O subclasa nu mosteneste constructorii superclasei, deci fiecare clasa are propriile sale functii constructor (definite explicit de programator sau generate de compilator). Nu se genereaza automat decat constructori fara argumente. Din acest motiv nu putem crea un obiect vector ordonat prin instructiunea urmatoare:

SortedVector a = new SortedVector(n); // nu exista constructor cu argument !

In Java constructorul implicit (fara argumente) generat pentru o subclasa apeleaza automat constructorul fara argumente al superclasei. Compilatorul Java genereaza automat o instructiune 'super(); ' inainte de prima instructiune din constructorul subclasei, daca nu exista un apel explicit si daca nu se apeleaza un alt constructor al subclasei prin 'this()'.

Initializarea variabilelor mostenite este realizata de constructorul superclasei, desi valorile folosite la initializare sunt primite ca argumente de constructorul subclasei. Este deci necesar ca un constructor din subclasa sa apeleze un constructor din superclasa. Apelul unui constructor din superclasa dintr-o subclasa se face folosind cuvantul cheie super ca nume de functie si nu este permisa apelarea directa, prin numele sau, a acestui constructor. Exemplu:

public class SortedVector extends Vector

public SortedVector (int max)

. . .

In exemplul anterior este necesara definirea constructorului fara argumente, care nu se mai genereaza automat daca se defineste (cel putin) un constructor cu argumente.

Daca subclasa are variabile in plus fata de superclasa, atunci ele vor fi initializate in constructorul subclasei. Exemplu:

public class SortedVector extends Vector

public SortedVector ( Comparator c)

public void addElement (Object obj)

}

Apelul constructorului superclasei ("super") trebuie sa fie prima instructiune din constructorul subclasei. Daca trebuie efectuate anterior anumite operatii, atunci ele vor fi grupate intr-o metoda statica (metode nestatice nu pot fi apelate inaintea unui constructor, pentru ca nu exista inca obiectul pentru care se apeleaza metoda):

// extragere cuvinte separate prin spatii dintr-un fisier text

class FileTokenizer extends StringTokenizer

// citire continut fisier text intr-un sir ( functie statica !)

static String readFile( String fname) catch (IOException ex)

return text;

}

// verificare metode clasa FileTokenizer

public static void main (String arg[])

}

In exemplul urmator clasa "MyQueue" (pentru liste de tip coada) este derivata din clasa "MyList" pentru liste simplu inlantuite. Cele doua clase folosesc in comun variabila "prim" (adresa de inceput a listei) si metoda "toString".

Metoda "add" este redefinita din motive de performanta, dar are acelasi efect: adaugarea unui nou element la sfarsitul listei.

public class MyQueue extends MyList

// adaugare la sfirsit coada (mai rapid ca in MyList)

public void add (Object v)

// eliminare obiect de la inceputul cozii

public Object del ()

// test daca coada goala

public boolean empty()

}

Din clasa "MyList" se poate deriva si o clasa stiva realizata ca lista inlantuita, cu redefinirea metodei de adaugare "add", pentru adaugare la inceput de lista, si cu o metoda de extragere (si stergere) obiect de la inceputul listei. Ideea este ca atat coada cat si stiva sunt cazuri particulare de liste. Avantajele reutilizarii prin extinderea unei clase sunt cu atat mai importante cu cat numarul metodelor mostenite si folosite ca atare este mai mare.

Derivare pentru creare de tipuri compatibile

Definirea unei noi clase este echivalenta cu definirea unui nou tip de date, care poate fi folosit in declararea unor variabile, argumente sau functii de tipul respectiv. Prin derivare se creeaza subtipuri de date compatibile cu tipul din care provin.

Tipurile superclasa si subclasa sunt compatibile, in sensul ca o variabila (sau un parametru) de un tip A poate fi inlocuit fara conversie explicita cu o variabila (cu un parametru) de un subtip D, iar trecerea de la o subclasa D la o superclasa A se poate face prin conversie explicita ("cast"). Conversia de tip intre clase incompatibile produce exceptia ClassCastException.

Exemple de conversii intre tipuri compatibile:

MyList a ; MyQueue q= new MyQueue();

a = q; // upcasting

q = (MyQueue) a ; // downcasting

Tipuri compatibile sunt tipurile claselor aflate intr-o relatie de descendenta (directa sau indirecta) pentru ca aceste clase au in comun metodele clasei de baza. Conversia explicita de la tipul de baza la un subtip este necesara pentru a putea folosi metodele noi ale subclasei pentru obiectele subclasei.

MyList

 


downcasting upcasting

MyQueue

 


De remarcat ca doua subtipuri ale unui tip comun A nu sunt compatibile ( de ex. tipurile Integer si Float nu sunt compatibile, desi sunt derivate din tipul Number). Trecerea intre cele doua tipuri nu se poate face prin operatorul de conversie, dar se poate face prin construirea altui obiect.


Procesul de extindere sau de specializare a unei clase poate continua pe mai multe niveluri, deci fiecare subclasa poate deveni superclasa pentru alte clase, si mai specializate.

Posibilitatea de creare a unor tipuri compatibile si de utilizare a unor functii polimorfice constituie unul din motivele importante pentru care se foloseste derivarea. Aplicarea repetata a derivarii produce ierarhii de tipuri si familii de clase inrudite.

Tipul superclasei este mai general decat tipurile subclaselor si de aceea se recomanda ca acest tip sa fie folosit la declararea unor variabile, unor argumente de functii sau componente ale unor colectii. In felul acesta programele au un caracter mai general si sunt mai usor de modificat.

De exemplu clasa Scanner (java.util), care recunoaste si converteste automat numere din format extern in format intern, poate prelua textul de scanat si dintr-un flux de intrare-iesire. In acest scop exista urmatorii constructori cu argumente de tip interfata:

public Scanner (InputStream source);

public Scanner (Readable source);

Tipurile InputStream si Readable sunt tipuri foarte generale, care include ca niste cazuri particulare fisiere text, siruri sau vectori de octeti s.a. In felul acesta se reduce numarul de constructori care ar fi fost necesar pentru fiecare clasa instantiabila derivata din clasa abstracta InputStream, de exemplu. Ierarhii de tipuri (primitive) exista si in limbajul C, permitand sa existe cate o singura functie matematica (sqrt, exp, log s.a.) pentru toate tipurile de numere (considerate ca subtipuri ale tipului double), in loc sa avem cate o functie pentru fiecare tip numeric (int, float, s.a.).

Putem constata in practica programarii cu obiecte ca unele superclase transmit subclaselor lor putina functionalitate (putine metode mostenite intocmai), deci motivul extinderii clasei nu este reutilizarea unor metode ci relatia speciala care apare intre tipurile subclasei si superclasei.

In general se definesc familii de clase, unele derivate din altele, care sunt toate compatibile ca tip cu clasa de la baza ierarhiei. Un exemplu este familia claselor exceptie, derivate direct din clasa Exception sau din subclasa RuntimeException. Instructiunea throw trebuie sa contina un obiect de un tip compatibil cu tipul Exception; de obicei un subtip ( IOException, NullPointerException s.a.).

O subclasa a clasei Exception nu adauga metode noi si contine doar constructori:

public class RuntimeException extends Exception

public RuntimeException(String s) // s= un mesaj suplimentar

}

Uneori superclasa este o clasa abstracta (neinstantiabila), care nu transmite metode subclaselor sale, dar creeaza clase compatibile cu clasa abstracta.

Un exemplu din Java 1.1 este clasa abstracta Dictionary, care contine metode utilizabile pentru orice clasa dictionar, indiferent de implementarea dictionarului:

get, put, remove, keys, s.a. Clasa de biblioteca Hashtable extinde clasa Dictionary, fiind un caz particular de dictionar, realizat printr-un tabel de dispersie. Putem sa ne definim si alte clase care sa extinda clasa Dictionary, cum ar fi un dictionar vector. Atunci cand scriem un program sau o functie vom folosi variabile sau argumente de tipul Dictionary si nu de tipul Hashtable sau de alt subtip. Exemplu de creare a unui dictionar cu numarul de aparitii al fiecarui cuvant dintr-un vector:

public static Dictionary wordfreq ( String v[])

return dic;

}

Rezultatul functiei "wordfreq" poate fi prelucrat cu metodele generale ale clasei Dictionary, fara sa ne intereseze ce implementare de dictionar s-a folosit in functie. Alegerea unei alte implementari se face prin modificarea primei linii din functie.

Exemplu de folosire a functiei:

public static void main (String arg[]) ;

Dictionary d= wordfreq(lista);

System.out.println (d); // scrie

}

Utilizarea unui tip mai general pentru crearea de functii si colectii cu caracter general este o tehnica specifica programarii cu obiecte. In Java (si in alte limbaje) aceasta tehnica a condus la crearea unui tip generic Object, care este tipul din care deriva toate celelalte tipuri clasa si care este folosit pentru argumentele multor functii (din clase diferite) si pentru toate clasele colectie predefinite (Vector, HashTable).

Clasa Object ca baza a ierarhiei de clase Java

In Java, clasa Object (java.lang.Object) este superclasa tuturor claselor JDK si a claselor definite de utilizatori. Orice clasa Java care nu are clauza extends in definitie este implicit o subclasa derivata direct din clasa Object. De asemenea, clasele abstracte si interfetele Java sunt subtipuri implicite ale tipului Object.

Clasa Object transmite foarte putine operatii utile subclaselor sale; de aceea in alte ierarhii de clase (din alte limbaje) radacina ierarhiei de clase este o clasa abstracta. Deoarece clasa Object nu contine nici o metoda abstracta, nu se impune cu necesitate redefinirea vreunei metode, dar in practica se redefinesc cateva metode.

Metoda "toString" din clasa Object transforma in sir adresa obiectului si deci trebuie sa fie redefinita in fiecare clasa cu date, pentru a produce un sir cu continutul obiectului. Metoda "toString" permite obtinerea unui sir echivalent cu orice obiect, deci trecerea de la orice tip la tipul String (dar nu prin operatorul de conversie ):

Date d = new Date();

String s = d.toString(); // dar nu si : String s = (String) d;

Metoda 'equals' din clasa Object considera ca doua variabile de tip Object sau de orice tip derivat din Object sunt egale daca si numai daca se refera la un acelasi obiect:

public boolean equals (Object obj)

Pe de alta parte, un utilizator al clasei String ( sau al altor clase cu date) se asteapta ca metoda 'equals' sa aiba rezultat true daca doua obiecte diferite (ca adresa) contin aceleasi date. De aceea, metoda 'equals' este rescrisa in clasele unde se poate defini o relatie de egalitate intre obiecte. Exemplu din clasa "Complex":

public boolean equals (Object obj)

Pentru a evita erori de programare de tipul folosirii metodei 'String.equals' cu argument de tip 'Complex', sau a metodei 'Complex.equals' cu argument de tip String se foloseste in Java operatorul instanceof, care are ca operanzi o variabila de un tip clasa si tipul unei (sub)clase si un rezultat boolean.

Redefinirea unei metode intr-o subclasa trebuie sa pastreze aceeasi semnatura cu metoda din superclasa, deci nu se pot schimba tipul functiei sau tipul argumentelor. Din acest motiv, argumentul metodei 'Complex.equals' este de tip Object si nu este de tip 'Complex', cum ar parea mai natural. Daca am fi definit metoda urmatoare:

public boolean equals (Complex cpx)

atunci ea ar fi fost considerata ca o noua metoda, diferita de metoda 'equals' mostenita de la clasa Object. In acest caz am fi avut o supradefinire ('overloading') si nu o redefinire de functii.

La apelarea metodei 'Complex.equals' poate fi folosit un argument efectiv de tipul 'Complex', deoarece un argument formal de tip superclasa poate fi inlocuit (fara conversie explicita de tip) printr-un argument efectiv de un tip subclasa. Exista deci mai multe metode 'equals' in clase diferite, toate cu argument de tip Object si care pot fi apelate cu argument de orice tip clasa.

Metoda 'equals' nu trebuie redefinita in clasele fara date sau in clasele cu date pentru care nu are sens comparatia la egalitate, cum ar fi clasele flux de intrare-iesire.

O variabila sau un argument de tip Object (o referinta la tipul Object) poate fi inlocuita cu o variabila de orice alt tip clasa, deoarece orice tip clasa este in Java derivat din si deci compatibil cu tipul Object (la fel cum in limbajul C o variabila sau un argument de tip void* poate fi inlocuita cu o variabila de orice tip pointer).

Polimorfism si legare dinamica

O functie polimorfica este o functie care are acelasi prototip, dar implementari (definitii) diferite in clase diferite dintr-o ierarhie de clase.

Asocierea unui apel de metoda cu functia ce trebuie apelata se numeste 'legare' ('Binding'). Legarea se poate face la compilare ("legare timpurie") sau la executie ('legare tarzie' sau 'legare dinamica'). Pentru metodele finale si statice legarea se face la compilare, adica un apel de metoda este tradus intr-o instructiune de salt care contine adresa functiei apelate.

Legarea dinamica are loc in Java pentru orice metoda care nu are atributul final sau static, metoda numita polimorfica. In Java majoritatea metodelor sunt polimorfice. Metodele polimorfice Java corespund functiilor virtuale din C++.

Metodele "equals", "toString" sunt exemple tipice de functii polimorfice, al caror efect depinde de tipul obiectului pentru care sunt apelate (altul decat tipul variabilei referinta). Exemple:

Object d = new Date();

Object f = new Float (3.14);

String ds = d.toString(); // apel Date.toString()

String fs = f.toString(); // apel Float.toString()

Mecanismul de realizare a legarii dinamice este relativ simplu, dar apelul functiilor polimorfice este mai putin eficient ca apelul functiilor cu o singura forma. Fiecare clasa are asociat un tabel de pointeri la metodele (virtuale) ale clasei. Clasa MyQueue va avea o alta adresa pentru functia 'add' decat clasa MyList.

Fiecare obiect contine, pe langa datele nestatice din clasa, un pointer la tabela de metode virtuale (polimorfice). Compilatorul traduce un apel de metoda polimorfica printr-o instructiune de salt la o adresa calculata in functie de continutul variabilei referinta si de definitia clasei. Apelul "q.add" pleaca de la obiectul referit de variabile 'q' la tabela metodelor clasei 'MyQueue' si de acolo ajunge la corpul metodei 'add' din clasa 'MyQueue'. Apelul "q.toString" conduce la metoda "toString" din superclasa ("MyList") deoarece este o metoda mostenita si care nu a fost redefinita. Apelul "q.equals" merge in sus pe ierahia de clase si ajunge la definitia din clasa Object, deoarece nici o subclasa a clasei Object nu redefineste metoda "equals".

Legatura dinamica se face de la tipul variabilei la adresa metodei apelate, in functie de tipul variabilei pentru care se apeleaza metoda. Fie urmatoarele variabile:

MyList a, q; a = new MyList(); q = new MyQueue();

Figura urmatoare arata cum se rezolva, la executie, apeluri de forma a.add(), q.add(), q.toString(), q.equals() s.a.


Solutia functiilor polimorfice este specifica programarii orientate pe obiecte si se bazeaza pe existenta unei ierarhii de clase, care contin toate metode (nestatice) cu aceeasi semnatura, dar cu efecte diferite in clase diferite. Numai metodele obiectelor pot fi polimorfice, in sensul ca selectarea metodei apelate se face in functie de tipul obiectului pentru care se apeleaza metoda. O metoda statica, chiar daca este redefinita in subclase, nu poate fi selectata astfel, datorita modului de apelare.

Alegerea automata a variantei potrivite a unei metode polimorfice se poate face succesiv, pe mai multe niveluri. De exemplu, apelul metodei "toString" pentru un obiect colectie alege functia ce corespunde tipului de colectie (vector, lista inlantuita, etc); la randul ei metoda "toString" dintr-o clasa vector apeleaza metoda "toString" a obiectelor din colectie, in functie de tipul acestor obiecte (String, Integer, Date, etc.).

O metoda finala (atributul final) dintr-o clasa A, care este membra a unei familii de clase, este o metoda care efectueaza aceleasi operatii in toate subclasele clasei A; ea nu mai poate fi redefinita. Exemplu:

public final boolean contains (Object elem)

O metoda statica poate apela o metoda polimorfica; de exemplu "Arrays.sort" apeleaza functia polimorfica "compareTo" (sau "compare"), al carei efect depinde de tipul obiectelor comparate, pentru a ordona un vector cu orice fel de obiecte.

Exemplu de functie pentru cautare secventiala intr-un vector de obiecte:

public static int indexOf (Object[] array, Object obj)

Pentru functia anterioara este esential ca metoda "equals" este polimorfica si ca se va selecta metoda de comparare din clasa careia apartine obiectul referit de variabila "obj" (aceeasi cu clasa elementelor din vectorul "array").

Structuri de date generice in POO

O colectie generica este o colectie de date (sau de referinte la date) de orice tip, iar un algoritm generic este un algoritm aplicabil unei colectii generice.

O colectie generica poate fi realizata ca o colectie de obiecte de tip Object iar o functie generica este o functie cu argumente formale de tip Object si/sau de un tip colectie generica. Pentru a memora intr-o colectie de obiecte si date de un tip primitiv (int, double, boolean s.a.) se pot folosi clasele "anvelopa" ale tipurilor primitive: Integer, Short, Byte, Float, Double,Boolean, Char.

Primele colectii generice din Java au fost clasele Vector, Hashtable si Stack dar din Java 1.2 sunt mai multe clase si sunt mai bine sistematizate.

Cea mai simpla colectie generica este un vector cu componente de tip Object:

Object [ ] a = new Object[10];

for (int i=0;i<10;i++)

a[i]= new Integer(i);

Vectorul intrinsec de obiecte generice se foloseste atunci cand continutul sau nu se modifica sau se modifica foarte rar, dar timpul de acces la elemente este important.

Clasa Vector ofera extinderea automata a vectorului si metode pentru diferite operatii uzuale: cautare in vector, afisare in vector, insertie intr-o pozitie data s.a. In plus, numarul de elemente din vector se obtine prin metoda "size" si nu trebuie transmis separat ca argument functiilor, ca in cazul unui vector intrinsec (variabila "length" nu spune cate elemente sunt in vector ci doar capacitatea vectorului). Exemplu de functie care creaza un sir cu valorile dintr-un vector, pe linii separate:

public static String arrayToString ( Object a[], int n)

public static String vectorToString ( Vector v)

Ceea ce numim o colectie de obiecte este de fapt o colectie de pointeri (referinte) la obiecte alocate dinamic, dar aceasta realitate este ascunsa de sintaxa Java. Exemplu de adaugare a unor siruri (referinte la siruri) la o colectie de tip Vector:

Vector v = new Vector (10); // aloca memorie pentru vector

for (int k=1;k<11;k++)

v.add ( k+""); // sau v.add (String.valueOf(k));

Exista posibilitatea de a trece de la un vector intrinsec la o clasa vector (metoda "Arrays.asList") si invers (metoda "toArray" din orice clasa colectie):

String a[]=;

Vector v = new Vector (Arrays.asList(a));

Object[] b = v.toArray();

Daca exista valori null in vectorul intrinsec, ele vor fi preluate de metoda "asList" in obiectul vector si pot produce exceptii la operatii asupra lor (si la ordonare).

Un alt avantaj al utilizarii de clase colectie fata de vectori intrinseci este acela ca se pot creea colectii de colectii, cu prelungirea actiunii unor functii polimorfice la subcolectii. De exemplu, metoda "toString" din orice colectie apeleaza repetat metoda "toString" pentru fiecare obiect din colectie (care, la randul lui, poate fi o colectie).

La extragerea unui element dintr-o colectie generica trebuie efectuata explicit conversia la tipul obiectelor introduse in colectie, pentru a putea aplica metode specifice tipului respectiv. Exemplu de creare a unui vector de siruri, modificarea sirurilor si afisarea vectorului :

public static void main (String args[])

System.out.println(v); // afisare vector

}

In Java, ca si in C, sunt posibile expresii ce contin apeluri succesive de metode, fara a mai utiliza variabile intermediare. Exemplu:

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

v.setElementAt ( ( (String)v.elementAt(i)).toLowerCase(), i);

Operatorul de conversie ("cast") are prioritate mai mica decat operatorul punct si de aceea trebuie folosite paranteze pentru elementul cu tip schimbat, inainte de a folosi acest element pentru apelul unei metode. Expresiile cu metode inlantuite aplicate unor elemente din colectii Java necesita astfel de paranteze, dar notatiile se simplifica in Java 5 daca se folosesc clase generice (cu tipuri declarate).

Sa consideram o multime de obiecte realizata ca tabel de dispersie ("hashtable"). Tabelul de dispersie va fi realizat ca un vector de vectori, deci ca un obiect de tip Vector, avand drept elemente obiecte de tip Vector (clasa Vector este si ea subclasa a clasei Object). O solutie mai buna este cea din clasa JDK Hashtable, in care se foloseste un vector de liste inlantuite. Fiecare vector (sau lista) contine un numar de "sinonime", adica elemente cu valori diferite pentru care metoda de dispersie a produs un acelasi indice in vectorul principal. Metoda de dispersie are ca rezultat restul impartirii codului de dispersie (produs de metoda "hashCode") prin dimensiunea vectorului principal. Numarul de sinonime din fiecare vector nu poate fi cunoscut si nici estimat, dar clasa Vector asigura extinderea automata a unui vector atunci cand este necesar.

public class HSet extends Vector

public HSet ()

// apartenenta obiect la multime

public boolean contains (Object el)

// adaugare obiect la multime (daca nu exista deja)

public boolean add (Object el)

// numar de obiecte din colectie

public int size()

// conversie continut colectie in sir

public String toString ()

return s;

}

Metodele polimorfice "toString", "contains" si "size" din clasa "HSet" apeleaza metodele cu acelasi nume din clasa Vector. Redefinirea metodei "toString" a fost necesara deoarece metoda "toString" mostenita de la clasa Vector (care o mosteneste de la o alta clasa) foloseste un iterator, care parcurge secvential toate elementele vectorului, dar o parte din ele pot avea valoarea null si genereaza exceptii la folosirea lor in expresii de forma v.toString() .

Subliniem ca variabilele unei colectii au tipul Object, chiar daca ele se refera la variabile de un alt tip (Integer, String, etc.); copilatorul Java nu "vede" decat tipul declarat (Object) si nu permite utilizarea de metode ale clasei din care fac parte obiectele reunite in colectie. Aceasta situatie este destul de frecventa in Java si face necesara utilizarea operatorului de conversie (tip) ori de cate ori o variabila de un tip mai general (tip clasa sau interfata) se refera la un obiect de un subtip al tipului variabilei. Exemplu:

Object obj = v.elementAt(i); // obiectul din vector poate avea orice tip

Float f = (Float) obj; // sau: Float f = (Float) v.elementAt(i);

De remarcat ca astfel de conversii nu modifica tipul obiectelor ci doar tipul variabilelor prin care ne referim la obiecte (tipul unui obiect nu poate fi modificat in cursul executiei).

Incercarea de conversie intre tipuri incompatibile poate fi semnalata la compilare sau la executie, printr-o exceptie. Exemple:

Float f = new Float(2.5); String s = (String) f; // eroare la compilare

Object obj = f; String s = (String) obj; // exceptie la executie

In al doilea caz tipul String este subtip al tipului Object si deci compilatorul Java permite conversia (nu stie tipul real al obiectului referit de variabila "obj").

Pentru a evita astfel de erori la executie s-au introdus din Java 1.5 colectii generice care permit specificarea tipului obiectelor din colectie la definirea colectiei; astfel nu se declara doar un obiect Vector ci se declara un vector de obiecte String sau Float sau de un alt tip:

Vector<String> a = new Vector<String>;



Politica de confidentialitate | Termeni si conditii de utilizare



DISTRIBUIE DOCUMENTUL

Comentarii


Vizualizari: 1938
Importanta: rank

Comenteaza documentul:

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

Creaza cont nou

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