Anotacijos Java kalboje Taip pat siūlome susipažinti: Lambda išraiškos Java į naują lygį Anotacijos yra labai svarbi Java dalis, įtraukta Java 5 versijoje. Pastaba: straipsnelyje paminimos ir kai kurios Java 8 galimybės darbui su anotacijomis. Ta2iau šis straipsnelis nėra išsamus ir pilnas anotacijų aprašymas: pagrindinis jo tikslas pristatyti anotacijas ir pademonstruoti darbą su jomis programų vykdymo metu. Anotacijos yra tam tikra metaduomenų rūšis, suteikinačios informaciją apie programą, tačiau pačios nesančios programos dalimi, tad jos neturi tiesioginio poveikio kodui, kurį apibūdina (anotuoja). Jas galima panaudoti įvairiai: Pavyzdžiui, programos formavimo (building) metu kompiliuojamas kodas, kuriami XML failai (pvz., diegimo deskriptoriai), sukompiliuotas kodas ir failai pakuojami į JAR failą ir kt. Formavimas paprastai atliekamas automatinių įrankių (pvz., Apache Ant arba Apache Maven). Tie įrankiai gali peržiūrėti Java kodą, ieškodami tam tikrų anotacijų ir pagal jas formuoti reikiamą versiją. Anotacijos tekste pradedamos eta ženklu (@), nurodančiu, kad po jo seka anotacija. Pavyzdžiui, kode dažnai matoma anotacija yra @Override public void toksMetodas() { } Anotacijos gali turėti vidinius elementus su priskirtais vardais arba anoniminius, pvz. @Autorius (asmuo="Jonas Jonaitis",data="05/31/2016") public class AnotuotaKlase () { } Jei elementas anotacijoje tėra vienas, jo vardą galima praleisti, pvz., @SuppressWarnings("unchecked") void manoMetodas() { ... } Pastaba: tam pačiam aprašui galima naudoti kelias anotacijas (net ir to paties tipo, tačiau tokios leistinos tin nuo Java 8), pvz., @Override @Autorius (asmuo="Petras Petraitis",data="05/31/2016") @Autorius (asmuo="Antanas Antanaitis",data="06/30/2016") public void toksMetodas() { } Anotacijas galima naudoti visiems aprašams: klasių, laukų, metodų ir kitų programos elementų. Java 8 anotacijas papildomai galima naudoti:
Galima susikurti savas anotacijas. Jų aprašas panašus į sąsajos (interface) aprašą tik prasideda @ ženklu, pvz., @Documented @interface Autorius { String asmuo(); String data(); Int revizija default 1; } Čia papildoma anotacija @Documented nurodo, kad anotacijos Autorius informacija turi atsirasti Javadoc generuotoje dokumentacijoje. Standartinės anotacijos Kažkiek anotacijų yra iš anksto apibrėžtos Java API; vienas jų naudoja kompiliatorius, o kai kurios taikomos kitoms anotacijoms. java.lang apibrėžia @Deprecated nurodo, kad elementas yra smerktinas ir neturėtų būti daugiau naudojamas. Kompiliatorius pateikia perspėjimą, jei jis panaudojamas. @Override nurodo, kad elementas perdengia elementą, aptašytą superklasėje. @SuppressWarnings nurodo, kad nereikia pateikti perspėjimų (nurodyto tipo, deprecation arba unchecked). Kitoms anotacijoms taikomos anotacijos yra metaanotacijos ir kažkiek jų aprašyta java.lang.annotation. @Retention nurodo, kaip yra saugoma anotacija:
@Documented nurodo, kad elementai bus dokumentuojami su Javadoc. @Inherited nurodo, kad anotacija yra paveldėta iš superklasės. Tokios anotacijos taikomos tik klasėms. @Repeatable - Java 8 įvesta anotacija, nurodanti, kad anotacija gali būti pasikartojanti (tam pačiam elementui). @Target papildomai apriboja anotacijos naudojimą nurodydama, kokiems elementams ji taikytina. Galimos reikšmės: ElementType.ANNOTATION_TYPE, ElementType.CONSTRUCTOR, ElementType.FIELD, ElementType.LOCAL_VARIABLE, ElementType.METHOD, ElementType.PACKAGE, ElementType.PARAMETER, ElementType.TYPE Anotacijų naudojimas Paimkime paprastą pavyzdį, - tam tikrai klasei realizuokime metodą: @Override public String toString() { return "Objekto atvaizdavimas: " + super.toString(); } Jame mes perdengiame toString() metodą savu objekto atvaizdavimu. Jei nebūtų nurodyta
@Override anotacija, kodas irgi veiktų. Tad koks anotacijos privalumas? @Override nurodo
kompiliatoriui, kad metodas yra perdengtas ir jei jokio tokio metodo nėra tėvinėje klasėje, būtų pateikiama
kompiliavimo klaida. Tarkim, kad mano pirštas nuslydo, ir vietoje Kodėl buvo įtrauktos anotacijos? Iki anotacijų metaduomenų pateikimui buvo plačiai naudojamas XML formatas. Tačiau jis labai prastai lipo prie kodo. XML buvo įtrauktas tam, kad atskirtų kongigūraciją nuo kodo kas truputį kelia įtarimą, nes tai dvi to paties ciklo dalys. Tarkim, kad norite nustatyti visai programai bendras konstantas (parametrus). Tada XML yra geresnis pasirinkimas, nes tai nesusiję su jokia konkrečia kodo dalimi. Tačiau jei norite atskirą metodą pateikti kaip servisą, geriau rinktis anotaciją, nes ji glaudžiau susieta su to metodo realizacija ir apie tai žino to metodo programuotojas. Kitas svarbus veiksnys buvo tas, kad anotacijos numato labai standartinį būdą metaduomenų aprašymui kode. Iki tol programuotojai tam naudojo įvairius savus būdus. Šiais laikais dažniausiai derinama XML ir anotacijų naudojimas, išnaudojant teigiamas abiejų būdų puses. Anotacijos yra galinga priemonė, kurią plačiai naudoja paketai (kaip spring ir Hibernate ypač loginimui ir validacijai). servlet 3.0 specifikacija įtraukė daug naujų anotacijų, ypač su saugumu susijusiems dalykams. Savo anotacijų kūrimas Jei programavote Java kalba, neabejotinai esate susipažinęs su anotacijomis. Tačiau, galbūt, jums neteko kurti savų anotacijų. Tam tereikia susikurti naują tipą naudojant @interface, nurodant visus metaduomenis. Tolimesniuose pavyzdžiuose naudosime tokį savo anotacijos Attr aprašą: @Target(ElementType.TYPE) @Retention(RetentionPolicy.RUNTIME) @interface Attr { boolean bPublic () default false; String Description(); } Pirmiausia matome, kad mūsų anotacija Attr pati turi dvi anotacijas: Anotacijas galima priskirti klasėms, metodams, parametrams ir laukams. Sukurkime demonstracinę klasę, kurioje visose situacijose panaudosime prieš tai aprašytą anotaciją Attr: @Attr(bPublic=true, Description="Priskirta anotacija") public static class myAnnotadedClass { @NonNull @Attr(Description="prisistatymo tekstas") String sPrisistatymas = "Tai mano anotuotos klases pavyzdys"; static int nInstance = 0; // neanotuotas kintamasis public myAnnotadedClass() { nInstance++; } @Override @Attr(bPublic=true, Description="prisistatymo tekstas") public String toString() { return "Objekto atvaizdavimas. Kopijos: " + nInstance + "\n" + sPrisistatymas + ". " + super.toString(); } // Metodas be anotacijos, bet anotuotas parametras public void pasisveikink (@Attr(Description="Vardo parametras") String pVardas) { System.out.println ("Labas, " + pVardas + ". Esu anotuota myAnnotadedClass!"); } public void emptyMethod () {} // jokios anotacijos } Šios klasės veikimo patikrinimui tinka toks kodo pavyzdys: myAnnotadedClass clKopija = new myAnnotadedClass(); System.out.println (clKopija.toString()); clKopija.pasisveikink("pone"); Taigi, kol kas nieko ypatinga, kitame skirsnelyje apžvelgsime, kaip galima paimti anotacijas vykdymo metu. Anotacijų pasiekimas iš kodo Java Reflection API leidžia pasiekti klases, sąsajas, laukus ir metodus vykdymo metu, kaip jų vardai nėra žinomi kompiliavimo metu. Jo priemonėmis taip pat galima kurti naujas objektų kopijas, iškviesti metodus bei gauti ar priskirti laukų reikšmes. Tai priemonė ir darbui su anotacijomis programos vykdymo metu. Nesigilinant į detales, priede straipsnelio pabaigoje pateikiamas pilnas darbo su anotacijomis pavyzdžio tekstas, kuriame rasite visų prieš tai aprašytų anotacijų tipo paėmimo iliustraciją. Anotacijos pakeitimas vykdymo metu Jei anotacija išsaugojama iki programos vykdymo (t.y. aprašyta kaip @Retention(RetentionPolicy.RUNTIME) ), jos aprašą galima pakeisti vykdymo metu. Pateiksime vieną variantų, iliustruojančių tą veiksmą aukščiau aprašytos klasės myAnnotadedClass anotacijos pakeitimui: private static void changeAnnotationDescription(Class pClass, Class pAnnotClass, final String pElem, final String pDescr) { @SuppressWarnings("unchecked") Annotation anot = pClass.getAnnotation(pAnnotClass); Object handler = Proxy.getInvocationHandler(anot); Field laukas; try { laukas = handler.getClass().getDeclaredField("memberValues"); } catch (NoSuchFieldException | SecurityException e) { throw new IllegalStateException(e); } laukas.setAccessible(true); Map Šį metodą galima panaudoti taip: changeAnnotationDescription(myAnnotadedClass.class, Attr.class, "Description", "Pakeista myAnnotadedClass anotacija"); Attr newAnnot = clKopija.getClass().getAnnotation(Attr.class); System.out.println("\nNaujai priskirta: " + newAnnot.Description()); Priedas Pateikiame pilną pradinį pavyzdžio, iliustruojančio straipsnyje aprašytus klausimus, tekstą. Jo pateikiami rezultatai apačioje. Pastaba: Jo vykdymui naudokite Java 8 aplinką, o į projektą įtraukite org.eclipse.jdt.annotation.NonNull paketą, jei naudojatės Eclipse IDE! Kiti gali j5 pakeisti kitu analogišku paketu. import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.reflect.Field; import java.lang.reflect.Method; import java.lang.reflect.Proxy; import java.lang.annotation.Annotation; import java.util.Map; import org.eclipse.jdt.annotation.NonNull; @SuppressWarnings("rawtypes") public class LabasAnotacija { @Retention(RetentionPolicy.RUNTIME) @interface Attr { boolean bPublic () default false; String Description(); } public static void main(String[] args) { System.out.println ("Labas, anotacija"); myAnnotadedClass clKopija = new myAnnotadedClass(); System.out.println (clKopija.toString()); clKopija.pasisveikink("pone"); boolean isNullAnnot, isOverAnnot, isAttrAnnotaded = clKopija.getClass().isAnnotationPresent(Attr.class); System.out.println ("Ar anuotuotos? LabasAnotacija: " + LabasAnotacija.class.isAnnotationPresent(Attr.class) + "; myAnnotadedClass: " + isAttrAnnotaded); if (isAttrAnnotaded) { System.out.println ("myAnnotadedClass: public: " + clKopija.getClass().getAnnotation(Attr.class).bPublic() + ", kas: " + clKopija.getClass().getAnnotation(Attr.class).Description()) ; } System.out.println("\nAnotuoti metodai:"); Method[] metodai = clKopija.getClass().getMethods(); for (Method m : metodai) { System.out.println ("Metodas " + m.getName() + ". "); isOverAnnot = m.isAnnotationPresent(Override.class); isAttrAnnotaded = m.isAnnotationPresent(Attr.class); System.out.print ("anotuotas kaip Override: " + isOverAnnot + ", su Attr anotacija: " + isAttrAnnotaded); if (isAttrAnnotaded) { Attr annot = m.getAnnotation(Attr.class); System.out.print ( "; anotacija: " + annot.Description()); } System.out.println(); } System.out.println("\nAnotuotas parametras:"); Method m; try { m = clKopija.getClass().getMethod("pasisveikink", String.class); Annotation[][] parameterAnnotations = m.getParameterAnnotations(); Class[] parameterTypes = m.getParameterTypes(); System.out.println ("Metodo " + m.getName() + " parametro anotacija:"); int k = 0; for (Annotation[] annotations : parameterAnnotations) { Class parameterType = parameterTypes[k++]; for (Annotation a : annotations) { if (a instanceof Attr) { Attr myAnnotation = (Attr) a; System.out.print("parametro tipas: " + parameterType.getName()); System.out.println(", anotacija " + myAnnotation.getClass().getName() + ": " + myAnnotation.Description()); } } } } catch (NoSuchMethodException e) { e.printStackTrace(); } System.out.println("\nAnotuoti laukai:"); Field laukai [] = clKopija.getClass().getFields(); for (Field f : laukai) { System.out.print("Laukas: " + f.getName() + "; tipas: " + f.getType() + ". "); isAttrAnnotaded = f.isAnnotationPresent(Attr.class); isNullAnnot = f.isAnnotationPresent(NonNull.class); System.out.print ("anotuotas kaip NonNull - " + isNullAnnot + ", su Attr anotacija: " + isAttrAnnotaded); if (isAttrAnnotaded) { System.out.print ( "; anotacija: " + f.getAnnotation(Attr.class).Description()); } System.out.println(); } changeAnnotationDescription(myAnnotadedClass.class, Attr.class, "Description", "Pakeista myAnnotadedClass anotacija"); Attr newAnnot = clKopija.getClass().getAnnotation(Attr.class); System.out.println("\nNaujai priskirta: " + newAnnot.Description()); System.out.println ("\nSudie, anotacija"); } // ------------------------------------------------- @SuppressWarnings("unchecked") private static void changeAnnotationDescription(Class pClass, Class pAnnotClass, final String pElem, final String pDescr) { Annotation anot = pClass.getAnnotation(pAnnotClass); Object handler = Proxy.getInvocationHandler(anot); Field laukas; try { laukas = handler.getClass().getDeclaredField("memberValues"); } catch (NoSuchFieldException | SecurityException e) { throw new IllegalStateException(e); } laukas.setAccessible(true); Map<String, Object> nariai; try { nariai = (Map<String, Object>) laukas.get(handler); System.out.println(nariai.toString()); Object oldValue = nariai.get(pElem); nariai.replace(pElem, pDescr); System.out.println("Pakeista " + pElem + ": " + oldValue.toString() + " -> " + pDescr); } catch (IllegalArgumentException | IllegalAccessException e) { throw new IllegalStateException(e); } } // -------------------------------------------------------- @Attr(Description="Priskirta anotacija") static class myAnnotadedClass { @NonNull @Attr(bPublic=true, Description="prisistatymo tekstas") public String sPrisistatymas = "Tai mano anotuotos klases pavyzdys"; static int nInstance = 0; // neanotuotas kintamasis public myAnnotadedClass() { nInstance++; } @Override @Attr(bPublic=true, Description="prisistatymo tekstas") public String toString() { return "Objekto atvaizdavimas. Kopijos: " + nInstance + "\n" + sPrisistatymas + ". " + super.toString(); } // Metodas be anotacijos, bet anotuotas parametras public void pasisveikink (@Attr(Description="Vardo parametras") String pVardas) { System.out.println ("Labas, " + pVardas + ". Esu anotuota myAnnotadedClass!"); } public void emptyMethod () {} // jokios anotacijos } } Rezultatai Pateiktas pavyzdys pateikia tokius rezultatus: Labas, anotacija Objekto atvaizdavimas. Kopijos: 1 Tai mano anotuotos klases pavyzdys. LabasAnotacija$myAnnotadedClass@c03315b5 Labas, pone. Esu anotuota myAnnotadedClass! Ar anuotuotos? LabasAnotacija: false; myAnnotadedClass: true myAnnotadedClass: public: false, kas: Priskirta anotacija Anotuoti metodai: Metodas equals. anotuotas kaip Override: false, su Attr anotacija: false Metodas hashCode. anotuotas kaip Override: false, su Attr anotacija: false Metodas toString. anotuotas kaip Override: false, su Attr anotacija: true; anotacija: prisistatymo tekstas Metodas pasisveikink. anotuotas kaip Override: false, su Attr anotacija: false Metodas emptyMethod. anotuotas kaip Override: false, su Attr anotacija: false Metodas getClass. anotuotas kaip Override: false, su Attr anotacija: false Metodas notify. anotuotas kaip Override: false, su Attr anotacija: false Metodas notifyAll. anotuotas kaip Override: false, su Attr anotacija: false Metodas wait. anotuotas kaip Override: false, su Attr anotacija: false Metodas wait. anotuotas kaip Override: false, su Attr anotacija: false Metodas wait. anotuotas kaip Override: false, su Attr anotacija: false Anotuotas parametras: Metodo pasisveikink parametro anotacija: parametro tipas: java.lang.String, anotacija $Proxy1: Vardo parametras Anotuoti laukai: Laukas: sPrisistatymas; tipas: class java.lang.String. anotuotas kaip NonNull - false, su Attr anotacija: true; anotacija: prisistatymo tekstas {bPublic=false, Description=Priskirta anotacija} Pakeista Description: Priskirta anotacija -> Pakeista myAnnotadedClass anotacija Naujai priskirta: Pakeista myAnnotadedClass anotacija Sudie, anotacija (c) 2016, Vartiklis. Visos teisės saugomos. Leidžiama naudoti tik asmeninės savišvietos tikslais. Bet koks platinimas bet kokiomis priemonemis, viso teksto arba atskiros jo dalies, draudžiamas!
Ka-ka-rie-kūūūū... | |