Как да опиша Java код с пояснения

Вероятно сте срещали ситуации, при които трябва да свържете метаданни (данни, които описват други данни) с класове, методи и / или други елементи на приложението. Например, вашият програмен екип може да се наложи да идентифицира недовършени класове в голямо приложение. За всеки недовършен клас метаданните вероятно ще включват името на разработчика, отговорен за завършването на класа, и очакваната дата на завършване на класа.

Преди Java 5, коментарите бяха единственият гъвкав механизъм, който Java трябваше да предложи за свързване на метаданни с елементи на приложението. Коментарите обаче са лош избор. Тъй като компилаторът ги игнорира, коментарите не са достъпни по време на изпълнение. И дори да бяха налични, текстът щеше да се анализира, за да се получат решаващи елементи от данни. Без да се стандартизира как са посочени елементите от данни, тези елементи на данни може да се окаже невъзможно да се анализират.

изтегляне Вземете кода Изтеглете изходния код за примери в този урок за Java 101. Създадено от Jeff Friesen за.

Нестандартни механизми за анотиране

Java предоставя нестандартни механизми за свързване на метаданни с елементи на приложението. Например transientзапазената дума ви позволява да анотирате (свързвате данни с) полета, които трябва да бъдат изключени по време на сериализацията.

Java 5 промени всичко, като въведе анотации , стандартен механизъм за свързване на метаданни с различни елементи на приложението. Този механизъм се състои от четири компонента:

  • Един @interfaceмеханизъм за обявяване на съответните типове.
  • Типове мета-анотации, които можете да използвате, за да идентифицирате елементите на приложението, за които се отнася тип анотация; за идентифициране на живота на анотация (екземпляр от тип анотация); и още.
  • Поддръжка за обработка на анотации чрез разширение на Java Reflection API (което ще бъде обсъдено в бъдеща статия), което можете да използвате за откриване на анотации по време на изпълнение на програмата и обобщен инструмент за обработка на анотации.
  • Стандартни типове анотации.

Ще обясня как да използвам тези компоненти, докато си проправяме път през тази статия.

Деклариране на типове анотации с @interface

Можете да декларирате тип анотация, като посочите @символа, последван веднага от interfaceзапазената дума и идентификатор. Например, Листинг 1 декларира прост тип анотация, който бихте могли да използвате, за да анотирате безопасен за нишки код.

Листинг 1:ThreadSafe.java

обществен @interface ThreadSafe {}

След декларирането на този тип анотация, добавете пред методите, които считате за безопасни за нишки, с екземпляри от този тип, като добавите @веднага, последвано от името на типа към заглавките на метода. Листинг 2 предлага прост пример, където main()методът е анотиран @ThreadSafe.

Листинг 2:AnnDemo.java (версия 1)

публичен клас AnnDemo {@ThreadSafe public static void main (String [] args) {}}

ThreadSafeекземплярите не предоставят метаданни, различни от името на типа анотация. Можете обаче да предоставите метаданни, като добавите елементи към този тип, където елемент е заглавката на метод, поставена в тялото на типа на анотацията.

Освен че нямат кодови тела, елементите са обект на следните ограничения:

  • Заглавката на метода не може да декларира параметри.
  • Заглавката на метода не може да осигури клауза за хвърляне.
  • Тип връщане на заглавката на метод трябва да бъде примитивен тип (например int), java.lang.String, java.lang.Class, на ENUM, тип анотация, или набор от един от тези видове. За типа връщане не може да бъде посочен друг тип.

Като друг пример, Листинг 3 представя тип ToDoанотация с три елемента, идентифициращи определена задача за кодиране, указвайки датата, когато задачата трябва да бъде завършена, и назовавайки кодера, отговорен за завършването на заданието.

Листинг 3:ToDo.java (версия 1)

публичен @interface ToDo {int id (); String finishDate (); String coder () по подразбиране "n / a"; }

Имайте предвид, че всеки елемент не декларира параметри или клаузи, има легален тип връщане ( intили String) и завършва с точка и запетая. Също така, последният елемент разкрива, че може да се зададе стойност по подразбиране; тази стойност се връща, когато анотация не присвоява стойност на елемента.

Листинг 4 използва ToDoза анотиране на незавършен метод на клас.

Листинг 4:AnnDemo.java (версия 2)

публичен клас AnnDemo {публична статична невалидна основна (String [] args) {String [] градове = {"Ню Йорк", "Мелбърн", "Пекин", "Москва", "Париж", "Лондон"}; сортиране (градове); } @ToDo (id = 1000, finishDate = "10/10/2019", coder = "John Doe") статично сортиране на празнота (обект [] обекти) {}}

Листинг 4 присвоява елемент на метаданни на всеки елемент; например 1000е присвоен на id. За разлика от coderтова idи finishDateтрябва да бъдат посочени елементи; в противен случай компилаторът ще докладва за грешка. Когато coderне е присвоена стойност, тя приема "n/a"стойността си по подразбиране .

Java предоставя специален String value()елемент, който може да се използва за връщане на разделен със запетая списък с елементи от метаданни. Листинг 5 демонстрира този елемент в рефакторирана версия на ToDo.

Листинг 5:ToDo.java (версия 2)

public @interface ToDo {String value (); }

Когато value()е единственият елемент на типа анотация, не е нужно да указвате valueи =оператора за присвояване, когато присвоявате низ на този елемент. Списък 6 демонстрира и двата подхода.

Листинг 6:AnnDemo.java (версия 3)

публичен клас AnnDemo {публична статична невалидна основна (String [] args) {String [] градове = {"Ню Йорк", "Мелбърн", "Пекин", "Москва", "Париж", "Лондон"}; сортиране (градове); } @ToDo (value = "1000,10 / 10/2019, John Doe") static void sort (Object [] обекти) {} @ToDo ("1000,10 / 10/2019, John Doe") статично булево търсене ( Object [] обекти, Object key) {return false; }}

Използване на типове мета-анотации - проблемът за гъвкавостта

Можете да анотирате типове (например класове), методи, локални променливи и др. Тази гъвкавост обаче може да бъде проблематична. Например, може да искате да ограничите само ToDoдо методи, но нищо не пречи да се използва за анотиране на други елементи на приложението, както е показано в Листинг 7.

Листинг 7:AnnDemo.java (версия 4)

@ToDo ("1000,10 / 10/2019, John Doe") публичен клас AnnDemo {public static void main (String [] args) {@ToDo (value = "1000,10 / 10/2019, John Doe") String [] градове = {"Ню Йорк", "Мелбърн", "Пекин", "Москва", "Париж", "Лондон"}; сортиране (градове); } @ToDo (value = "1000,10 / 10/2019, John Doe") static void sort (Object [] обекти) {} @ToDo ("1000,10 / 10/2019, John Doe") статично булево търсене ( Object [] обекти, Object key) {return false; }}

In Listing 7, ToDo is also used to annotate the AnnDemo class and cities local variable. The presence of these erroneous annotations might confuse someone reviewing your code, or even your own annotation processing tools. For the times when you need to narrow an annotation type’s flexibility, Java offers the Target annotation type in its java.lang.annotation package.

Target is a meta-annotation type — an annotation type whose annotations annotate annotation types, as opposed to a non-meta-annotation type whose annotations annotate application elements, such as classes and methods. It identifies the kinds of application elements to which an annotation type is applicable. These elements are identified by Target’s ElementValue[] value() element.

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 представи aptинструмент за обработка на анотации по обобщен начин. Java 6 е мигрирала aptфункционалността на своя javacинструмент за компилация и Java 7 е оттеглена apt, което впоследствие е премахнато (като се започне с Java 8).

Стандартни типове анотации

Заедно с Target, Retention, Documented, и Inherited, Java 5 въведена java.lang.Deprecated, java.lang.Overrideи java.lang.SuppressWarnings. Тези три типа анотации са предназначени да се използват само в контекст на компилатор, поради което техните политики за задържане са настроени на SOURCE.

Оттеглено