Adnotarile sunt o modalitate prin care putem scapa de mult cod boilerplate si prin care putem prelua controlul unor Clase, Metode si Campuri inainte de a fi folosite in contextul aplicatiei noastre.
Ele intra in categoria decoratorilor care adauga metadate peste aceste elemente si ne permit, prin minunatul java reflection, sa le manipulam dupa voia noastra, fara a fi nevoie de un pas suplimentar de discriminare.
De asemenea, toate facilitatile Java Reflection sunt indreptate spre scopul de a ne oferi un control ierarhic cat mai high-level asupra claselor pe care le manipulam.
Cum construim o adnotare
Pentru a incepe sa ne adaugam adnotarile, ar trebui sa intelegem urmatoarele proprietati ale acestora. Exemplul cu care vom lucra:
@Retention(RetentionPolicy.RUNTIME) @Target(ElementType.FIELD) public @interface Tutorial { }
Asadar, observam doua adnotari ce semnifica proprietatile adnotarii noastre Tutorial:
- @Retention – reprezinta durata de viata a adnotarii noastre. Valorile posibile sunt:
- RetentionPolicy.SOURCE – adnotarea noastra va disparea la compilare. Folositi aceasta adnotare in cazul in care vreti sa adaugati informatii despre codul vostru sursa si sa le interpretati intr-un tool extern.
- RetentionPolicy.CLASS – adnotarea va ajunge in clasa compilata, insa nu este obligatoriu ca ea sa fie interpretata de JVM. Folositi aceasta politica de retentie daca urmariti sa va construiti propriul classloader care va avea acces la decoratorii vostri.
- RetentionPolicy.RUNTIME – adnotarea va ajunge in clasa compilata si va fi obligatoriu vizibila in interiorul JVM-ului. Aceasta este alegerea pentru acest tutorial.
- @Target – Reprezinta constrangerea utilizarii adnotarilor, practic, pe ce se poate aplica aceast nou decorator. Valorile posibile sunt:
- ElementType.ANNOTATION_TYPE – adnotarea se poate aplica pe alte adnotari (Inception much).
- ElementType.CONSTRUCTOR – adnotarea se poate aplica pe constructorii claselor voastre.
- ElementType.FIELD – adnotarea se poate aplica pe campurile unei clase.
- ElementType.LOCAL_VARIABLE – adnotarea se poate aplica pe campurile locale ale clasei.
- ElementType.METHOD – adnotarea se poate aplica pe o metoda.
- ElementType.MODULE – adnotarea se poate aplica pe un modul Java (Java 9+).
- ElementType.PACKAGE – adnotarea se poate aplica pe un pachet Java.
- ElementType.PARAMETER – adnotarea se poate aplica pe un parametru dintr-un constructor sau metoda.
- ElementType.TYPE – adnotarea se poate aplica pe un Java Type precum clase, interfete, enum sau alte adnotari.
- ElementType.TYPE_PARAMETER – adnotarea se poate aplica pe variabilele generice de tip.
- ElementType.TYPE_USE – adnotarea se poate aplica pe folosirea unui Java Type, precum instantierea, castarea sau implementarea unei interfete.
Pentru a continua explicatiile de mai sus, trebuie folosim cuvantul cheie @interface pentru a preciza explicit ca ceea ce vom defini in interiorul acestei interfete, si anume: o adnotare.
Pssst…: Daca vrei sa inveti Java de la 0, sau vrei sa iti aprofundezi cunostintele, am cursul perfect pentru tine – vezi aici
Cum adaugam parametri in adnotarea noastra
Utilitatea unei adnotari este formata din doua parti:
- Faptul ca putem discrimina o clasa / metoda / un camp si putem sa ne folosim de aceasta informatie atunci cand il/o folosim.
- Faptul ca putem parametriza acest discriminator prin valorile parametrilor sai.
Un exemplu de astfel de use-case este adnotarea @Table parte din JPA care ne permite sa specificam numele tabelului SQL pe care vrem sa il creeze Hibernate pentru entitatea noastra (@Table(„studenti”)).
Pentru a adauga parametri adnotarii noastre, putem folosi urmatoarea structura:
@Retention(RetentionPolicy.RUNTIME) @Target(ElementType.FIELD) public @interface Tutorial { public String value() default ""; }
Dupa cum poti vedea, parametri se adauga precum metodele in interefete, fara implementare si cu o valoare default. Pentru a trimite acest parametru intr-un camp adnotat cu @Tutorial putem scrie:
public class AnnotatedClass { @Tutorial("parametru") private int age; }
Si asignam valoarea campului value din adnotarea @Tutorial valorea parametru. Aceasta metoda este utilizabila doar in cazul in care avem un singur parametru in adnotare, altfel, va trebui o notatie de forma:
public class AnnotatedClass { @Tutorial(value = "parametru", param1 = 1, param2 = "parametru") private int age; }
In cazul in care nu specificam o valoare a unui parametru, acesta va lua automat valoarea default din definitia adnotarii.
Cum adaugam parametri in adnotarea noastra
Folosind Java.Lang.Reflect vom citi campurile din clasa noastra, il vom cauta pe cel adnotat cu @Tutorial si ii vom afisa valoarea parametrului value astfel:
public class SpringtestApplication { public static void main(String[] args) { Class<AnnotatedClass> ourClass = AnnotatedClass.class; for (Field field : ourClass.getDeclaredFields()) { if (field.isAnnotationPresent(Tutorial.class)) { Annotation ourAnnotation = field.getAnnotation(Tutorial.class); Tutorial tutorialAnnotation = (Tutorial) ourAnnotation; System.out.println("Am gasit adnotarea @Tutorial"); System.out.println(tutorialAnnotation.value()); } } } }
Traducerea liniilor de cod de mai sus este urmatoarea:
Pentru fiecare camp din clasa AnnotatedClass sub instanta sa, ourClass, cautam adnotarea Tutorial prin apelul isAnnotationPresent(adnotareCautata). Cand o gasim, castam adnotarea gasita in Tutorial.class si ii citim parametri.
Putem face acest lucru atat la nivel de metoda, cat si la nivel de Observer daca dorim sa reactionam la prezenta uneia sau mai multor adnotari sau valori.
Concluzie
Adnotarile Java ne permit sa extindem functionalitatile pe care ni le dorim in limbajul Core, adaugand decoratori peste clase, metode si campuri. Odata ce procesul descris mai sus este aplicat de 5-6 ori, iti va fi foarte usor sa il aplici in cazurile din viata reala, unde consideri ca ar fi util.
Nu te arunca la decorarea excesiva – deoarece ea prezinta doua riscuri:
- Codul devine mai greu de inteles pentru colegii tai.
- Testarea unitara a adnotarilor este dificila – direct proportionala cu numarul de parametri pe care ii vei declara in interior.
Sper sa iti fie de ajutor, spor la joaca!