Kako opisati Java kôd s napomenama

Vjerojatno ste se susreli sa situacijama u kojima trebate povezati metapodatke (podatke koji opisuju druge podatke) s klasama, metodama i / ili drugim elementima aplikacije. Na primjer, vaš će programski tim možda trebati identificirati nedovršene razrede u velikoj aplikaciji. Za svaki nedovršeni razred metapodaci bi vjerojatno sadržavali ime programera odgovornog za završetak nastave i očekivani datum završetka klase.

Prije Jave 5, komentari su bili jedini fleksibilni mehanizam koji je Java trebala ponuditi za povezivanje metapodataka s elementima aplikacije. Međutim, komentari su loš izbor. Budući da ih kompajler ignorira, komentari nisu dostupni tijekom izvođenja. Pa čak i da su dostupni, tekst bi se morao raščlaniti kako bi se dobili ključni podaci. Bez standardizacije načina na koji su specificirane stavke podataka, te bi se stavke podataka mogle pokazati nemoguće analizirati.

preuzimanje Preuzmite kod Preuzmite izvorni kod za primjere u ovom vodiču za Java 101. Stvorio Jeff Friesen za.

Nestandardni mehanizmi bilježenja

Java pruža nestandardne mehanizme za povezivanje metapodataka s elementima aplikacije. Na primjer, transientrezervirana riječ omogućuje vam bilježenje (pridruživanje podataka) poljima koja će biti izuzeta tijekom serializacije.

Java 5 promijenila je sve uvodeći napomene , standardni mehanizam za povezivanje metapodataka s raznim elementima aplikacije. Ovaj se mehanizam sastoji od četiri komponente:

  • @interfaceMehanizam za proglašavanje vrste napomena.
  • Vrste meta-bilješki koje možete koristiti za prepoznavanje elemenata aplikacije na koje se primjenjuje vrsta bilješke; identificirati vijek trajanja bilješke (primjer vrste bilješke); i više.
  • Podrška za obradu napomena putem proširenja Java Reflection API-ja (o kojoj će se raspravljati u budućem članku), a koju možete koristiti za otkrivanje napomena o izvršavanju programa i generaliziranog alata za obradu napomena.
  • Standardne vrste bilješki.

Objasnit ću vam kako koristiti ove komponente dok se probijamo kroz ovaj članak.

Deklariranje vrsta bilješki s @ sučeljem

Možete primijeniti vrstu bilješke navodeći @simbol koji odmah slijedi interfacerezervirana riječ i identifikator. Na primjer, Popis 1 deklarira jednostavnu vrstu bilješke koju biste mogli upotrijebiti za označavanje koda sigurnog za nit.

Popis 1:ThreadSafe.java

javni @interface ThreadSafe {}

Nakon deklariranja ove vrste bilješke, prefiksirajte metode koje smatrate nitima sigurne kod instanci ove vrste tako što ćete @odmah dodati ime tipa u zaglavlja metode. Popis 2 nudi jednostavan primjer gdje je main()metoda označena @ThreadSafe.

Popis 2:AnnDemo.java (verzija 1)

javna klasa AnnDemo {@ThreadSafe javna statička void glavna (String [] args) {}}

ThreadSafeprimjerci ne daju nikakve metapodatke osim naziva vrste bilješke. Međutim, možete dostaviti metapodatke dodavanjem elemenata ovoj vrsti, gdje je element zaglavlje metode smješteno u tijelo tipa bilješke.

Osim što nemaju tijela koda, elementi podliježu sljedećim ograničenjima:

  • Zaglavlje metode ne može deklarirati parametre.
  • Zaglavlje metode ne može pružiti klauzulu bacanja.
  • Vrsta povrata Metoda zaglavlju mora biti primitivni tip (npr int), java.lang.String, java.lang.Class, An enum, vrsta su primjedba, ili niz jednu od tih vrsta. Za tip povrata ne može se navesti nijedan drugi tip.

Kao drugi primjer, Popis 3 predstavlja ToDovrstu bilješke s tri elementa koja identificiraju određeni posao kodiranja, navodeći datum kada posao treba završiti i imenujući kodera odgovornog za dovršavanje posla.

Popis 3:ToDo.java (verzija 1)

javni @interface ToDo {int id (); String finishDate (); String coder () zadani "n / a"; }

Imajte na umu da svaki element ne prijavljuje nikakve parametre ili klauzulu klauzule, ima pravni tip povratka ( intili String) i završava točkom i zarezom. Također, završni element otkriva da se može odrediti zadana povratna vrijednost; ova se vrijednost vraća kada bilješka elementu ne dodijeli vrijednost.

Popis 4 koristi ToDoza bilježenje nedovršene metode klase.

Popis 4:AnnDemo.java (verzija 2)

javna klasa AnnDemo {javna statička praznina glavna (String [] args) {String [] gradovi = {"New York", "Melbourne", "Peking", "Moskva", "Pariz", "London"}; razvrstati (gradovi); } @ToDo (id = 1000, finishDate = "10/10/2019", coder = "John Doe") statička sortiranje praznina (objekt [] objekti) {}}

Popis 4 svakom elementu dodjeljuje stavku metapodataka; na primjer, 1000dodijeljen je id. Za razliku od coder, su idi finishDateelementi moraju se navesti; u suprotnom će kompajler prijaviti pogrešku. Kada joj codernije dodijeljena vrijednost, poprima zadanu "n/a"vrijednost.

Java nudi poseban String value()element koji se može koristiti za vraćanje popisa stavki metapodataka odvojenih zarezom. Popis 5 pokazuje ovaj element u prepravljenoj verziji ToDo.

Popis 5:ToDo.java (verzija 2)

public @interface ToDo {String value (); }

Kada value()je jedini element vrste bilješke, ne morate navesti valuei =operator dodjele prilikom dodjeljivanja niza ovom elementu. Popis 6 pokazuje oba pristupa.

Popis 6:AnnDemo.java (verzija 3)

javna klasa AnnDemo {javna statička praznina glavna (String [] args) {String [] gradovi = {"New York", "Melbourne", "Peking", "Moskva", "Pariz", "London"}; razvrstati (gradovi); } @ToDo (value = "1000,10 / 10/2019, John Doe") static void sort (Object [] objects) {} @ToDo ("1000,10 / 10/2019, John Doe") statičko logičko pretraživanje ( Objekt [] objekti, Object key) {return false; }}

Korištenje vrsta meta-bilješki - problem fleksibilnosti

Možete bilježiti vrste (npr. Klase), metode, lokalne varijable i još mnogo toga. Međutim, ta fleksibilnost može biti problematična. Na primjer, možda biste htjeli ograničiti ToDosamo na metode, ali ništa ga ne sprječava da se koristi za bilješke drugih elemenata aplikacije, kao što je pokazano u Popisu 7.

Popis 7:AnnDemo.java (verzija 4)

@ToDo ("1000,10 / 10/2019, John Doe") javna klasa AnnDemo {public static void main (String [] args) {@ToDo (value = "1000,10 / 10/2019, John Doe") String [] gradovi = {"New York", "Melbourne", "Peking", "Moskva", "Pariz", "London"}; razvrstati (gradovi); } @ToDo (value = "1000,10 / 10/2019, John Doe") static void sort (Object [] objects) {} @ToDo ("1000,10 / 10/2019, John Doe") statičko logičko pretraživanje ( Objekt [] objekti, Object key) {return false; }}

U ToDopopisu 7, također se koristi za bilježenje AnnDemoklase i citieslokalne varijable. Prisutnost ovih pogrešnih napomena može zbuniti nekoga tko pregledava vaš kôd ili čak vaše vlastite alate za obradu napomena. Za trenutke kada trebate suziti fleksibilnost tipa bilješke, Java nudi Targetvrstu bilješke u svom java.lang.annotationpaketu.

Targetje vrsta meta-bilješki  - vrsta bilješke čije bilješke bilježe vrste bilješki, za razliku od one koja nije meta-bilješka čije bilješke bilježe elemente aplikacije, poput klasa i metoda. Identificira vrste elemenata aplikacije na koje je primjenjiva vrsta bilješke. Te elemente prepoznaje element Target's' ElementValue[] value().

java.lang.annotation.ElementType is an enum whose constants describe application elements. For example, CONSTRUCTOR applies to constructors and PARAMETER applies to parameters. Listing 8 refactors Listing 5’s ToDo annotation type to restrict it to methods only.

Listing 8:ToDo.java (version 3)

import java.lang.annotation.ElementType; import java.lang.annotation.Target; @Target({ElementType.METHOD}) public @interface ToDo { String value(); }

Given the refactored ToDo annotation type, an attempt to compile Listing 7 now results in the following error message:

AnnDemo.java:1: error: annotation type not applicable to this kind of declaration @ToDo("1000,10/10/2019,John Doe") ^ AnnDemo.java:6: error: annotation type not applicable to this kind of declaration @ToDo(value="1000,10/10/2019,John Doe") ^ 2 errors

Additional meta-annotation types

Java 5 introduced three additional meta-annotation types, which are found in the java.lang.annotation package:

  • Retention indicates how long annotations with the annotated type are to be retained. This type’s associated java.lang.annotation.RetentionPolicy enum declares constants CLASS (compiler records annotations in class file; virtual machine doesn’t retain them to save memory — default policy), RUNTIME (compiler records annotations in class file; virtual machine retains them), and SOURCE (compiler discards annotations).
  • Documented indicates that instances of Documented-annotated annotations are to be documented by javadoc and similar tools.
  • Inherited indicates that an annotation type is automatically inherited.

Java 8 introduced the java.lang.annotation.Repeatable meta-annotation type. Repeatable is used to indicate that the annotation type whose declaration it (meta-)annotates is repeatable. In other words, you can apply multiple annotations from the same repeatable annotation type to an application element, as demonstrated here:

@ToDo(value = "1000,10/10/2019,John Doe") @ToDo(value = "1001,10/10/2019,Kate Doe") static void sort(Object[] objects) { }

This example assumes that ToDo has been annotated with the Repeatable annotation type.

Processing annotations

Annotations are meant to be processed; otherwise, there’s no point in having them. Java 5 extended the Reflection API to help you create your own annotation processing tools. For example, Class declares an Annotation[] getAnnotations() method that returns an array of java.lang.Annotation instances describing annotations present on the element described by the Class object.

Listing 9 presents a simple application that loads a class file, interrogates its methods for ToDo annotations, and outputs the components of each found annotation.

Listing 9:AnnProcDemo.java

import java.lang.reflect.Method; public class AnnProcDemo { public static void main(String[] args) throws Exception { if (args.length != 1) { System.err.println("usage: java AnnProcDemo classfile"); return; } Method[] methods = Class.forName(args[0]).getMethods(); for (int i = 0; i < methods.length; i++) { if (methods[i].isAnnotationPresent(ToDo.class)) { ToDo todo = methods[i].getAnnotation(ToDo.class); String[] components = todo.value().split(","); System.out.printf("ID = %s%n", components[0]); System.out.printf("Finish date = %s%n", components[1]); System.out.printf("Coder = %s%n%n", components[2]); } } } }

After verifying that exactly one command-line argument (identifying a class file) has been specified, main() loads the class file via Class.forName(), invokes getMethods() to return an array of java.lang.reflect.Method objects identifying all public methods in the class file, and processes these methods.

Method processing begins by invoking Method’s boolean isAnnotationPresent(Class annotationClass) method to determine if the annotation described by ToDo.class is present on the method. If so, Method’s T getAnnotation(Class annotationClass) method is called to obtain the annotation.

The ToDo annotations that are processed are those whose types declare a single String value() element (see Listing 5). Because this element’s string-based metadata is comma-separated, it needs to be split into an array of component values. Each of the three component values is then accessed and output.

Compile this source code (javac AnnProcDemo.java). Before you can run the application, you’ll need a suitable class file with @ToDo annotations on its public methods. For example, you could modify Listing 6’s AnnDemo source code to include public in its sort() and search() method headers. You’ll also need Listing 10’s ToDo annotation type, which requires the RUNTIME retention policy.

Listing 10:ToDo.java (version 4)

import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; @Target({ElementType.METHOD}) @Retention(RetentionPolicy.RUNTIME) public @interface ToDo { String value(); }

Compile the modified AnnDemo.java and Listing 10, and execute the following command to process AnnDemo’s ToDo annotations:

java AnnProcDemo AnnDemo

If all goes well, you should observe the following output:

ID = 1000 Finish date = 10/10/2019 Coder = John Doe ID = 1000 Finish date = 10/10/2019 Coder = John Doe

Processing annotations with apt and the Java compiler

Java 5 predstavila je aptalat za generaliziranu obradu bilješki. Java 6 je migrirala aptfunkcionalnost u svoj javacalat za kompajliranje, a Java 7 je zastarjela apt, što je naknadno uklonjeno (počevši od Jave 8).

Standardne vrste bilješki

Uz Target, Retention, Documentedi Inherited, Java 5 uvela java.lang.Deprecated, java.lang.Overridei java.lang.SuppressWarnings. Ove tri vrste bilješki dizajnirane su za upotrebu samo u kontekstu kompajlera, zbog čega su postavljene njihove politike zadržavanja SOURCE.

Zastarjelo