Cara menerangkan kod Java dengan anotasi

Anda mungkin pernah menghadapi situasi di mana anda perlu mengaitkan metadata (data yang menerangkan data lain) dengan kelas, kaedah, dan / atau elemen aplikasi lain. Sebagai contoh, pasukan pengaturcaraan anda mungkin perlu mengenal pasti kelas yang belum selesai dalam aplikasi besar. Untuk setiap kelas yang belum selesai, metadata mungkin akan menyertakan nama pemaju yang bertanggungjawab untuk menyelesaikan kelas dan tarikh tamat kelas yang dijangka.

Sebelum Java 5, komen adalah satu-satunya mekanisme fleksibel yang ditawarkan Java untuk mengaitkan metadata dengan elemen aplikasi. Walau bagaimanapun, komen adalah pilihan yang tidak baik. Oleh kerana penyusun tidak menghiraukannya, komen tidak tersedia pada waktu proses. Dan walaupun tersedia, teks harus dihuraikan untuk mendapatkan item data penting. Tanpa menyeragamkan bagaimana item data ditentukan, item data ini mungkin terbukti mustahil untuk dihuraikan.

muat turun Dapatkan kod Muat turun kod sumber untuk contoh dalam tutorial Java 101 ini. Dicipta oleh Jeff Friesen untuk.

Mekanisme anotasi tidak standard

Java menyediakan mekanisme tidak standard untuk mengaitkan metadata dengan elemen aplikasi. Sebagai contoh, transientperkataan yang dikhaskan membolehkan anda memberi anotasi (kaitkan data dengan) bidang yang akan dikecualikan semasa bersiri.

Java 5 mengubah segalanya dengan memperkenalkan anotasi , mekanisme standard untuk mengaitkan metadata dengan pelbagai elemen aplikasi. Mekanisme ini terdiri daripada empat komponen:

  • Satu @interfacemekanisme untuk mengisytiharkan jenis anotasi.
  • Jenis anotasi meta, yang boleh anda gunakan untuk mengenal pasti elemen aplikasi yang digunakan untuk jenis anotasi; untuk mengenal pasti jangka hayat anotasi (contoh jenis anotasi); dan banyak lagi.
  • Sokongan untuk pemprosesan anotasi melalui peluasan ke Java Reflection API (akan dibahas dalam artikel mendatang), yang dapat Anda gunakan untuk mengetahui anotasi runtime program, dan alat umum untuk memproses anotasi.
  • Jenis anotasi standard.

Saya akan menerangkan bagaimana menggunakan komponen-komponen ini semasa kita menjalani artikel ini.

Menyatakan jenis anotasi dengan @interface

Anda boleh menyatakan jenis anotasi dengan menentukan @simbol yang segera diikuti oleh interfacekata yang dicadangkan dan pengecam. Sebagai contoh, Penyenaraian 1 mengisytiharkan jenis anotasi sederhana yang mungkin anda gunakan untuk menganotasi kod selamat utas.

Penyenaraian 1:ThreadSafe.java

awam @interface ThreadSafe {}

Setelah mengisytiharkan jenis anotasi ini, awalkan kaedah yang anda anggap selamat di thread dengan contoh jenis ini dengan membuat persiapan @segera diikuti dengan nama jenis ke tajuk kaedah. Penyenaraian 2 menawarkan contoh mudah di mana main()kaedahnya diberi penjelasan @ThreadSafe.

Penyenaraian 2:AnnDemo.java (versi 1)

kelas awam AnnDemo {@ThreadSafe public static void main (String [] args) {}}

ThreadSafecontoh tidak memberikan metadata selain nama jenis anotasi. Walau bagaimanapun, anda boleh memberikan metadata dengan menambahkan elemen ke jenis ini, di mana elemen adalah tajuk kaedah yang diletakkan di badan jenis anotasi.

Selain tidak mempunyai badan kod, elemen dikenakan sekatan berikut:

  • Tajuk kaedah tidak dapat menyatakan parameter.
  • Header kaedah tidak dapat memberikan klausa lemparan.
  • Jenis pulangan kaedah pengepala perlu menjadi jenis primitif (contohnya, int), java.lang.String, java.lang.Class, yang enum, jenis penjelasan, atau pelbagai salah satu daripada jenis-jenis ini. Tidak ada jenis lain yang dapat ditentukan untuk jenis pengembalian.

Sebagai contoh lain, Penyenaraian 3 menyajikan ToDojenis anotasi dengan tiga elemen yang mengenal pasti tugas pengekodan tertentu, menentukan tarikh bila tugas itu akan selesai, dan menamakan pengekod yang bertanggungjawab untuk menyelesaikan pekerjaan.

Penyenaraian 3:ToDo.java (versi 1)

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

Perhatikan bahawa setiap elemen tidak menyatakan parameter atau klausa lemparan, mempunyai jenis pengembalian yang sah ( intatau String), dan diakhiri dengan titik koma. Juga, elemen akhir menunjukkan bahawa nilai kembali lalai dapat ditentukan; nilai ini dikembalikan apabila anotasi tidak memberikan nilai pada elemen.

Penyenaraian 4 digunakan ToDountuk memberi penjelasan kaedah kelas yang belum selesai.

Penyenaraian 4:AnnDemo.java (versi 2)

kelas awam AnnDemo {public static void main (String [] args) {String [] city = {"New York", "Melbourne", "Beijing", "Moscow", "Paris", "London"}; menyusun (bandar); } @ToDo (id = 1000, finishDate = "10/10/2019", coder = "John Doe") semacam kekosongan statik (objek [] objek) {}}

Penyenaraian 4 memberikan item metadata untuk setiap elemen; sebagai contoh, 1000ditugaskan untuk id. Tidak seperti coder, elemen iddan finishDateelemen mesti dinyatakan; jika tidak, penyusun akan melaporkan ralat. Apabila codertidak diberikan nilai, ia menganggap "n/a"nilai lalai .

Java menyediakan String value()elemen khas yang dapat digunakan untuk mengembalikan senarai item metadata yang dipisahkan koma. Penyenaraian 5 menunjukkan elemen ini dalam versi refactored dari ToDo.

Penyenaraian 5:ToDo.java (versi 2)

public @interface ToDo {Nilai rentetan (); }

Bilakah value()satu-satunya elemen jenis anotasi, anda tidak perlu menentukan valuedan =operator penugasan ketika memberikan rentetan pada elemen ini. Penyenaraian 6 menunjukkan kedua-dua pendekatan.

Penyenaraian 6:AnnDemo.java (versi 3)

kelas awam AnnDemo {public static void main (String [] args) {String [] city = {"New York", "Melbourne", "Beijing", "Moscow", "Paris", "London"}; menyusun (bandar); } @ToDo (nilai = "1000,10 / 10/2019, John Doe") semacam kekosongan statik (Objek [] objek) {} @ToDo ("1000,10 / 10/2019, John Doe") carian boolean statik ( Objek [] objek, Kekunci objek) {return false; }}

Menggunakan jenis meta-anotasi - masalah kelenturan

Anda boleh memberi anotasi jenis (contohnya, kelas), kaedah, pemboleh ubah tempatan dan banyak lagi. Walau bagaimanapun, fleksibiliti ini boleh menimbulkan masalah. Sebagai contoh, anda mungkin mahu menyekat ToDohanya kaedah, tetapi tidak ada yang menghalangnya daripada digunakan untuk memberi anotasi elemen aplikasi lain, seperti yang ditunjukkan dalam Penyenaraian 7.

Penyenaraian 7:AnnDemo.java (versi 4)

@ToDo ("1000,10 / 10/2019, John Doe") kelas awam AnnDemo {public static void main (String [] args) {@ToDo (value = "1000,10 / 10/2019, John Doe") Rentetan [] bandar = {"New York", "Melbourne", "Beijing", "Moscow", "Paris", "London"}; menyusun (bandar); } @ToDo (nilai = "1000,10 / 10/2019, John Doe") semacam kekosongan statik (Objek [] objek) {} @ToDo ("1000,10 / 10/2019, John Doe") carian boolean statik ( Objek [] objek, Kekunci objek) {return false; }}

Dalam Penyenaraian 7, ToDojuga digunakan untuk memberi penjelasan pada AnnDemokelas dan citiespemboleh ubah tempatan. Kehadiran anotasi yang salah ini mungkin membingungkan seseorang yang mengkaji kod anda, atau bahkan alat pemprosesan anotasi anda sendiri. Untuk saat-saat di mana anda perlu menyekat fleksibiliti jenis anotasi, Java menawarkan Targetjenis anotasi dalam java.lang.annotationpakejnya.

Targetadalah jenis anotasi meta - jenis  anotasi yang anotasinya jenis anotasi, berbanding dengan jenis anotasi bukan meta yang anotasinya menerangkan elemen aplikasi, seperti kelas dan kaedah. Ini mengenal pasti jenis elemen aplikasi yang boleh digunakan untuk jenis anotasi. Unsur-unsur ini dikenal pasti oleh Target's ElementValue[] value()elemen.

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 memperkenalkan aptalat untuk memproses anotasi secara umum. Java 6 memindahkan aptfungsi ke dalam javacalat penyusunnya, dan Java 7 tidak lagi digunakan apt, yang kemudiannya dihapus (dimulai dengan Java 8).

Jenis anotasi standard

Bersama-sama dengan Target, Retention, Documented, dan Inherited, Jawa 5 diperkenalkan java.lang.Deprecated, java.lang.Override, dan java.lang.SuppressWarnings. Ketiga-tiga jenis anotasi ini dirancang untuk digunakan dalam konteks penyusun sahaja, sebab itulah dasar pengekalannya ditetapkan SOURCE.

Tidak digunakan lagi