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

Lectia 4-2011 Java

Documentul prezintă tipurile generice din Java, inclusiv tipurile bounded și wildcard. Sunt prezentate exemple de clase generice și metode, precum și subclase generice. De asemenea, sunt explicate anotațiile din Java.

Încărcat de

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

Lectia 4-2011 Java

Documentul prezintă tipurile generice din Java, inclusiv tipurile bounded și wildcard. Sunt prezentate exemple de clase generice și metode, precum și subclase generice. De asemenea, sunt explicate anotațiile din Java.

Încărcat de

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

Codul generic - tipul bounded Tipurile bounded nu exista in cazul codului legacy.

Toate obiectele in cod legacy sunt implicit marginite superior de Object. Astfel, orice metoda poate accepta orice tip de obiect, ceea ce determina folosirea conversiei pentru a incerca prevenirea exceptiilor. In schimb, tipurile generice elimina conversiile, dar aceasta introduce o limitare si anume tipurile generice nu pot fi specificate. Presupunem ca dorim sa cream o clasa ce returneaza suma, ca un double, a unui sir de numere de oricare dintre tipurile: int, double sau float. Aceasta ar insemna crearea a trei clase cu cod aproape identic.
class Sum<Integer> { // calcularea sumei } class Sum<Float> { // calcularea sumei } class Sum<Double> { // calcularea sumei }

In Java 5.0 se introduce tipul bounded pentru a elimina acest neajuns. Sintactic, in programarea generica, tipul bounded este folosit in sintaxa <T extends superclasa>, unde T poate fi orice subtip al superclasei sau chiar superclasa (superclasa este de tip bounded). In cazul presupunerii nostre Integer, Float si Double sunt toate subclase ale clasei Number, care este clasa de tip bounded.
public class Sum<T extends Number> { T[] nums; // nums este un sir de tip Number sau subclasa a sa // constructor Sum (T[] o){ nums = o; } // tipul returnat este double in oricare dintre cazuri double sumTotal() { double sum = 0.0; for (int i=0; i < nums.length; i++) sum += nums[i].doubleValue(); } return sum;

public static void main(String[]args){ Integer a[]={1,2,3}; Double d[]={8.0,5.0,6.0}; Sum<Integer> intregi=new Sum<Integer>(a); System.out.println(intregi.sumTotal()); Sum<Double> reali=new Sum<Double>(d); System.out.println(reali.sumTotal()); }

Clasa Number se numeste bounded si compilatorul stie ca obiectele de tip T pot apela orice metoda declarata de Number. In plus, compilatorul va preveni crearea oricaror obiecte Sum fara a fi numerice (instanta a lui Number sau derivate din aceasta). Presupunem, in continuare, ca dorim sa comparam sumele calculate in doua clase Sum. Pentru aceasta am definit in clasa Sum metoda String compare( Sum<T>). Aceasta, insa, ridica o problema deoarece Sum<Integer> nu poate fi comparata cu Sum<Number>, chiar daca Integer este subtip al lui Number. Pentru rezolvarea acestei probleme Java face apel la

elementul ? (wildcard), ce specifica un tip necunoscut. Cu acesta semnatura metodei devine: String compare( Sum<?>) si acum accepta orice tip de obiect Sum. Codul, cu adaugarea acestei metode devine:
public class Sum<T extends Number> { T[] nums; // nums este un sir de tip Number sau subclasa a sa // constructor Sum (T[] o){ nums = o; } // tipul returnat este double in oricare dintre cazuri double sumTotal() { double sum = 0.0; for (int i=0; i < nums.length; i++) sum += nums[i].doubleValue(); } return sum;

String comparare(Sum<?> ob){ if (this.sumTotal()<ob.sumTotal()) return "mai mic"; else return "mai mare sau egal"; } public static void main(String[]args){ Integer a[]={1,2,3}; Double d[]={8.0,5.0,6.0}; Sum<Integer> intregi=new Sum<Integer>(a); System.out.println(intregi.sumTotal()); Sum<Double> reali=new Sum<Double>(d); System.out.println(reali.sumTotal()); System.out.println(intregi.comparare(reali)); }

Si cu wildcardurile putem folosi tipurile marginite (bounded) intr-o sintaxa de tipul <? extends superclasa>. Lint warnings Lint reprezinta o functionalitate a Javei ce ne permite sa identificam cod nesigur. Lint warnings se genereaza cand incercam sa compilam cod nesigur. Recompilarea unui cod cu Xlint:unchecked, va genera avertismentele de cod nesigur indicand locul in care pot aparea erori la executie. Declararea unei clase generice Cand declaram o clasa generica trebuie sa furnizam parametrul tip. Clasa generica se va executa indiferent de tipurile parametru transmise. Cand cream o instanta a unui tip generic, argumentul tip transmis parametrului tip trebuie sa fie tip clasa. className<lista-arg-tip> varName= new className<lista-argtip>(lista-arg-constr); Iata un exemplu:
class GenericClass<T> { private T genericValue; public GenericClass(T genericValue) {

this.genericValue = genericValue; } public T get() { return genericValue; } public void set(T genericValue) { this.genericValue = genericValue; }

Unde T este parametrul tip ce va fi inlocuit in momentul in care clasa generica va fi apelata, ca in exemplul urmator.
import java.util.*; public class GenericExample { public static void main(String[] args) { GenericClass<String> gen1 = new GenericClass<String>("Hello"); GenericClass<Integer> gen2 = new GenericClass<Integer>(8); GenericClass<Double> gen3 = new GenericClass<Double>(2.34); System.out.println("gen1.genericValue este " + gen1.get()); System.out.println("gen2.genericValue este " + gen2.get()); System.out.println("gen3.genericValue este " + gen3.get()); gen1.set("World"); System.out.println("gen1.genericValue is " + gen1.get()); }}

Codul declara trei obiecte de tip GenericClass, dar de versiuni diferite. Crearea metodelor generice si a constructorilor Constructorul poate fi generic chiar daca clasa nu este generica. Cand declaram o metoda in interiorul clasei generice aceasta este capabila sa foloseasca parametrul tip al clasei. Putem apela o metoda generica in metoda main() fara a specifica argumentul tip. Urmatoarea secventa, bazata pe exemplul anterior, va genera o eroare la compilare:
gen1.set(gen2.get());

deoarece cele doua obiecte nu au aceeasi valoare a parametrului tip. Subclase generice Clasele generice pot forma ierarhii de clase. Cand o clasa generica mosteneste o alta clasa generica tipul parametru al superclasei trebuie sa fie declarat de subclasa. Aceasta trebuie realizata chiar daca tipul nu este folosit in subclasa.
class SubClasaGenerica<T,V> extends ClasaGenerica<T>

Cand o clasa generica mosteneste o clasa negenerica nu trebuie sa specificam niciun argument.
class SubClasaGenerica<T> extends ClasaNegenerica

In continuare vom da un exemplu pentru a ilustra primul caz.


public class Sum { public static void main(String[] args) { GenericClass<String> gen1 = new GenericClass<String>("Hello"); GenericClass<Integer> gen2 = new GenericClass<Integer>(8); GenericClass<Double> gen3 = new GenericClass<Double>(2.34); System.out.println("gen1.genericValue este " + gen1.get()); System.out.println("gen2.genericValue este " + gen2.get());

System.out.println("gen3.genericValue este " + gen3.get()); gen1.set("World"); System.out.println("gen1.genericValue este " + gen1.get()); // urmatoarea linie ar cauza eroare la compilare // gen1.set(gen2.get()); GenericSubclass<String, Integer> subGen = new GenericSubclass("Andy", 40); boolean b1 = subGen instanceof GenericClass; boolean b2 = subGen instanceof GenericSubclass; // urmatoarea linie ar cauza eroare la compilare // boolean b3 = subGen instanceof GenericClass<String>; b1); b2); System.out.println("este subGen instanta a GenericClass: " + System.out.println("este subGen instanta a GenericSubclass: " + GenericClass<Integer> genInt1 = null; GenericClass<String> genInt2 = null; if (b1) { // urmatoarea linie ar cauza eroare la compilare // GenericClass<Integer> genInt1 = (GenericClass<String>) // urmatoarea linie NU genereaza eroare la compilare genInt2 = (GenericClass<String>) subGen;

subGen;

} } }

class GenericClass<T> { private T genericValue; public GenericClass(T genericValue) { this.genericValue = genericValue; } public T get() { return genericValue; } public void set(T genericValue) { this.genericValue = genericValue; } } class GenericSubclass<T, V> extends GenericClass<T> { private V subType; public GenericSubclass(T genericValue, V subType) { super(genericValue); } this.subType = subType;

public V getSubtype() {

return subType; } }

Operatorul instanceof ne permite sa determinam daca un obiect este o instanta a unei clase. Acelasi lucru poate fi facut si pentru clasele generice. In codul anterior subGen este o instanta a lui GenericClass. Linia ce-l declara pe b3 va genera o eroare la compilare. Tipul GenericClass<String> nu exista la rulare pentru ca informatiile despre tipul obiectelor sunt indepartate la rulare printrun proces numit type erasure. Putem converti o instanta a unei clase generice daca sursa si destinatia au argumente de tip identice sau compatibile. Anotatii Java ne permite sa incastram metadate (date despre date) intr-un fisier sursa, proces numit anotare. Dezvoltatorii folosesc anotarile pentru a asocia metadate elementelor de program ca: declaratii de pachete, declaratii de tip, clase, metode, campuri, parametri si variabile locale. Compilatorul stocheaza metadatele in fisierele clasa. Atunci cand este cazul poate examina metadatele pentru a determina cum sa interactioneze cu elementele de program. Observatie: Anotarile nu modifica codul sursa compilat Anotarile sunt folosite in: Analiza codului, care poate fi: o analiza simpla, un tool de metadate poate utiliza anotatii pentru a construi un catalog ce specifica tipurile de intrare si tipurile returnate. o analiza complexa, un tool de metadate gestioneaza si sincronizeza clase multiple si dependentele in EJB. Verificari la compilare: compilatorul poate utiliza metadate anotate pentru a indica, spre exemplu, o metoda ce suprascrie o alta metoda din superclasa. Documentare: metadate anotate permit dezvoltatorilor sa includa informatii despre faptul ca metodele sunt incheiate, sau metodele sunt dependente de alte metode, sau o clasa este referita de o alta clasa Exemplu de anotatie:
@interface Annotate{ int value(); String desc(); }

Declaram un tip anotatie numit Annotate. Cuvantul cheie @interface informeaza compilatorul ca un tip anotatie va fi creat si ca are doua elemente: value() si desc(). Cand aplicam o anotare, elementelor continute li se asigneaza valori. Spre exemplu:
@Annotate (value=1, desc=exemplu) public static void metoda(){. Am pus anotarea Annotate in legatura cu metoda metoda(). membrului value, iar valoarea exemplu lui desc.

Valoarea 100 este asignata

In versiunile de Java de pina la 5 anotarile erau facute pentru toolul Javadoc. Incepand cu J2SE 5 avem la dispozitie un mecanism extensibil de metadate ce ne permite sa cream tool-uri specializate pentru a procesa date in aplicatii. Un exemplu de tool specializat este XDoclet-ul ce analizeaza anotatiile si gestioneaza dependentele, prin parsarea Javadocului si intrumente de generare a claselor. Xdoclet-ul este valabil pe site-ul: http://xdoclet.sourceforge.net. Java suporta urmatoarele categorii de anotatii:

marker, care este o anotatie goala ce serveste la a marca un element de program asociat, pentru procesari ulterioare (@MyMarkerAnnotation) single-value, contine un singur membru (@MySingleValue(130)) full, contine mai multi membri, fiecare asignat cu o valoare (@MyFullAnnotation(myFull="mf")) Polita de retentie se refera la valabilitatea la rulare a anotatilor la nivelul compilatorului si al masinii virtuale. Java furnizeaza urmatoarele polite de retentie: CLASS, anotatia este stocata de compilator intr-un fisier class, dar nu este disponibila prin JVM, la rulare RUNTIME, asemanator lui CLASS, dar este disponibil la rulare. Aceasta ofera cea mai buna persistenta, permitand prinderea si stocarea datelor anotate pentru viitoare prelucrari. SOURCE, este tinuta doar in fisierul sursa si este nefolosita de compilator Politele de retentie Java sunt incapsulate in enumerarea java.lang.annotation.RetentionPolicy. Pentru a specifica o polita de retentie vom folosi anotatia predefinita @Retention cu sintaxa generala: @Retention(polita), unde polita specifica numele exact al politei. Ex:
@Retention(RetentionPolicy.RUNTIME)

Java pune la dispozitie anotatii predefinite, dar permite si utilizatorilor sa-si defineasca proprile anotatii. Tipurile predefinite de anotatii includ: @Deprecated, marcheaza o anotatie ce identifica o declaratie invechita, mai precis, inlocuita de una noua. Aceasta anotatie se afla in java.lang.annotation. Tag-ul Javadoc @deprecated serveste aceluiasi scop. Comentariul de dupa tag va specifica declaratia ce a inlocuit declaratia anotata. Cand compilatorul intalneste stringul @deprecated la inceputul unei linii de documentare va plasa un atribut Deprecated in fisierul class pentru clasa sau metoda specificate. Este recomandabil sa folosim tipul anotare si tag-ul Javadoc in tandem atat pentru compilare cat si pentru documentare.
class Deprecate { /** * @deprecated folositi in schimb metoda newMethod() */ @Deprecated public void oldMethod() { System.out.println("salut"); } // newMethod inlocuieste oldMethod public void newMethod() { System.out.println("salut nou"); }

public class DeprecatedTest extends Deprecate { public static void main(String[] args) { DeprecatedTest test = new DeprecatedTest(); // un apel al metodei oldMethod va crea o avertizare la compilare test.oldMethod(); } }

Tag-ul Javadoc @deprecated va crea o intrare in fisierul de documentare, intrare ce va fi folosita pentru afisarea mesajului folositi in schimb metoda newMethod(). @Override, specifica ca o metoda trebuie sa suprascrie o metoda din superclasa. Daca suprascrierea esueaza compilatorul va genera o eroare la compilare. Nu are membri de initializat. Fie urmatorul exemplu:
public class OverrideTest { @Override public String toString() { return "salut"; } @Override public String toString(String s) { return "salut, " + s; } }

Clasa OverrideTest extinde, dupa cum se stie clasa Object, care contine o metoda toString() fara parametri, dar nu are si una cu parametri. De aceea anotatia @Override de la cea de-a doua metoda va produce, la compilare, o eroare. @SuppressWarnings, specifica ca una sau mai multe atentionari vor fi eliminate pentru un element astfel anotat, precum si in toate elementele de program continute in el. Obs: numele atentionarilor variaza de la compilator la compilator. Compilatoarele ignora numele de avertizari pe care nu le recunoaste. Ele pot genera atentionari daca o anotatie contine un nume nerecunoscut de atentionare. Pentru a crea o anotatie @SuppressWarnings trebuie ca intre paranteze rotunde sa avem o valoare, care trebuie sa fie un sir de stringuri, ce specifica una sau mai multe atentionari ce vor fi suprimate in elementul anotat. Fie urmatorul exemplu:
import java.util.*; public class SuppressTest { @SuppressWarnings(value={"unchecked"}) public void preGenericsMethod() { // aceasta este o lista raw List wordList = new ArrayList(); // fara anotatia SuppressWarnings urmatoarea linie de cod ar // emite o atentionare la compilare wordList.add("foo");

} }

@Target, este o meta-anotatie si indica elementele de program, precum clasele si metodele, la care o anumita anotatie definita de utilizator se poate aplica. Se previne astfel exploatarea anotatiilor. Declaratia @Target precede intotdeauna definirea unei anotatii. Nu trebuie utilizat @Target daca tipul anotatie se aplica tuturor elementelor de program. Trebuie intotdeauna utilizat Target pentru a verifica tipurile de anotatie definite de utilizator. Are argumente care trebuie sa fie constante din enumeratia ElementType si anume: ANNOTATION_TYPE, CONSTRUCTOR, FIELD, LOCAL_VARIABLE, METHOD, PACKAGE, PARAMETER sau TYPE.
import java.lang.annotation.*; @Target({ElementType.METHOD, ElementType.CONSTRUCTOR}) @interface Custom { String value(); }

In exemplul anterior dorim sa definim o anotatie numita Custom, care sa se poata aplica doar metodelor si constructorilor. @Retention, este o meta-anotatie care specifica daca compilatorul retine si foloseste o anotatia definita de utilizator in fisierul class compilat al clasei anotate. Are ca argument o valoare din RetentionPolicy. Aceasta anotatie precede intotdeauna definirea unei anotatii utilizator. Anotatia @Retention trebuie sa apara inaintea definirii anotatiei utilizator. @Target va apare inainte de @Retention.
import java.lang.annotation.*; @Target({ElementType.TYPE, ElementType.METHOD, ElementType.CONSTRCUTOR, ElementType.ANNOTATION_TYPE}) @Retention(RetentionPolicy.RUNTIME) @interface Custom { String value (); }

In exemplu anterior anotatia definita de utilizator va fi valida si la rulare @Documented, este o meta-anotatie si marcheaza o anotatie definita de utilizator pentru a fi documentata in Javadoc. In general este util sa folosim aceasta anotatie atunci cand avem si anotatia @Retention si aceasta este setata la valoarea RetentionPolicy.RUNTIME. Anotatia apare prima in lista anotatiilor ce insotesc o anotatie utilizator.
import java.lang.annotation.*; @Documented @Target({ElementType.TYPE, ElementType.METHOD, ElementType.CONSTRUCTOR, ElementType.ANNOTATION_TYPE}) @Retention(RetentionPolicy.RUNTIME) @interface Custom{ String value (); }

@Inherited: este o meta-anotatie si este folosita pentru ca tipurile anotare nu sunt automat mostenite de subclase. Spre exemplu, daca o superclasa este deprecated, subclasa nu mosteneste statutul de invechire. Pentru a ne asigura ca subclasele mostenesc tipul anotare trebuie sa procedam ca in exemplul urmator:
@Documented @Inherited @Target({ElementType.TYPE, ElementType.METHOD, ElementType.CONSTRUCTOR, ElementType.ANNOTATION_TYPE}) @Retention(RetentionPolicy.RUNTIME) @interface Custom { String value(); }

Anotatii definite de dezvoltatori Utilizatorii pot sa-si creeze propriile anotatii prin cuvantul rezervat @interface. Sintaxa generala este:
public @interface Anotatie{ tip_returnat membru() default valoare; }

Anotatiile definite de utilizator pot include valori predefinite.

Clasa Class este parte a pachetului java.lang. Aceasta clasa nu are un constructor public si de aceea JVM construieste automat obiecte de tip Class la rulare. Reflectia ne permite sa accesam informatii despre constructori, campuri si metodele unei clase, la rulare. Permite, de asemenea, sa determinam tipul anotatiei asociate unei clase, camp sau metode. Pachetul java.lang.reflect contine clase si interfete ce ne permit sa folosim reflectia. Incepand cu J2SE 5.0 in acest pachet este inclusa si interfata AnnotatedElement, ce ne permite acesul la metodele anotate si la introspectia tipurilor anotate. Observatie: reflectia, la nivelul anotatiilor, poate fi facuta daca anotatia are proprietatea de retentie setata la runtime. Interfata AnnotatedElement declara urmatoarele metode: getAnnotation(), returneaza un tip de anotatie specificat pentru un element de program. Daca elementul nu are specificata vreo anotatie returneaza null. Sintaxa este: NameOfAnnotationTypeClass instanceOfAnnotationTypeClass =
instanceOfAnnotatedElementInterface.getAnnotation(NameOfAnnotationTyp eClass.class);

getAnnotations(), returneaza toate anotatiile unui element de program. Daca elementul nu are anotatii returneza un sir de lungime zero. Sintaxa este: Annotation[ isAnnotation(), verifica daca un obiect clasa este un tip anotatie. Sintaxa este: isAnnotationPresent(), ne permite sa verificam daca un anume tip de anotatie este prezent pentru un element de program. Nu poate fi folosit decat pentru anotatii marker. Sintaxa este: boolean variable =
instanceOfProgramElement.isAnnotationPresent(NameOfAnnotationType.cla ss); Metoda annotationType() returneaza tipul unei anotatii. Sintaxa este: annotationTypeClassName variable = instanceofAnnotationInterface.annotationType(); boolean variable = instanceOfClassObject.isAnnotation(); ] instanceOfAnnotation[] getAnnotations(); = instanceOfAnnotatedElementInterface.

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