Anda mungkin pernah mengalami situasi di mana Anda perlu mengaitkan metadata (data yang mendeskripsikan data lain) dengan kelas, metode, dan / atau elemen aplikasi lainnya. Misalnya, tim pemrograman Anda mungkin perlu mengidentifikasi kelas yang belum selesai dalam aplikasi yang besar. Untuk setiap kelas yang belum selesai, metadata kemungkinan akan menyertakan nama pengembang yang bertanggung jawab untuk menyelesaikan kelas dan tanggal penyelesaian kelas yang diharapkan.
Sebelum Java 5, komentar adalah satu-satunya mekanisme fleksibel yang ditawarkan Java untuk mengaitkan metadata dengan elemen aplikasi. Namun, komentar adalah pilihan yang buruk. Karena kompilator mengabaikannya, komentar tidak tersedia pada waktu proses. Dan bahkan jika tersedia, teks harus diurai untuk mendapatkan item data penting. Tanpa standarisasi bagaimana item data ditentukan, item data ini mungkin terbukti tidak mungkin untuk diurai.
unduh Dapatkan kodenya Unduh kode sumber untuk contoh dalam tutorial Java 101 ini. Dibuat oleh Jeff Friesen untuk.Mekanisme anotasi nonstandar
Java menyediakan mekanisme nonstandar untuk mengaitkan metadata dengan elemen aplikasi. Misalnya, transient
kata khusus memungkinkan Anda menganotasi (mengaitkan data dengan) bidang yang akan dikecualikan selama serialisasi.
Java 5 mengubah segalanya dengan memperkenalkan anotasi , mekanisme standar untuk menghubungkan metadata dengan berbagai elemen aplikasi. Mekanisme ini terdiri dari empat komponen:
- Sebuah
@interface
mekanisme untuk menyatakan jenis anotasi. - Jenis anotasi meta, yang dapat Anda gunakan untuk mengidentifikasi elemen aplikasi yang menerapkan jenis anotasi; untuk mengidentifikasi umur anotasi (contoh dari jenis anotasi); dan lainnya.
- Dukungan untuk pemrosesan anotasi melalui ekstensi ke Java Reflection API (akan dibahas di artikel mendatang), yang dapat Anda gunakan untuk menemukan anotasi waktu proses program, dan alat umum untuk memproses anotasi.
- Jenis anotasi standar.
Saya akan menjelaskan cara menggunakan komponen ini saat kami mengerjakan artikel ini.
Mendeklarasikan jenis anotasi dengan @interface
Anda dapat mendeklarasikan jenis anotasi dengan menentukan @
simbolnya segera diikuti oleh interface
kata yang dipesan dan pengenal. Misalnya, Listing 1 mendeklarasikan jenis anotasi sederhana yang mungkin Anda gunakan untuk membuat anotasi kode aman-thread.
Daftar 1:ThreadSafe.java
public @interface ThreadSafe {}
Setelah mendeklarasikan jenis anotasi ini, berikan awalan pada metode yang Anda anggap aman untuk thread dengan instance jenis ini dengan @
segera diikuti dengan nama jenis ke header metode. Kode 2 menawarkan contoh sederhana di mana main()
metode dianotasi @ThreadSafe
.
Daftar 2:AnnDemo.java
(versi 1)
kelas publik AnnDemo {@ThreadSafe public static void main (String [] args) {}}
ThreadSafe
Instance tidak menyediakan metadata selain nama jenis anotasi. Namun, Anda dapat menyediakan metadata dengan menambahkan elemen ke jenis ini, di mana elemen adalah header metode yang ditempatkan di badan jenis anotasi.
Selain tidak memiliki badan kode, elemen tunduk pada batasan berikut:
- Header metode tidak dapat mendeklarasikan parameter.
- Header metode tidak dapat memberikan klausa lemparan.
- Metode header tipe kembali harus menjadi tipe primitif (misalnya,
int
),java.lang.String
,java.lang.Class
, enum, tipe anotasi, atau sebuah array dari salah satu jenis. Tidak ada tipe lain yang dapat ditentukan untuk tipe pengembalian.
Sebagai contoh lain, Kode 3 menyajikan ToDo
jenis anotasi dengan tiga elemen yang mengidentifikasi pekerjaan pengkodean tertentu, menentukan tanggal kapan pekerjaan akan selesai, dan penamaan pembuat kode yang bertanggung jawab untuk menyelesaikan pekerjaan.
Kode 3:ToDo.java
(versi 1)
Public @interface ToDo {int id (); String finishDate (); Pembuat kode string () default "n / a"; }
Perhatikan bahwa setiap elemen mendeklarasikan tidak ada parameter atau klausa lemparan, memiliki tipe pengembalian resmi ( int
atau String
), dan diakhiri dengan titik koma. Juga, elemen terakhir mengungkapkan bahwa nilai pengembalian default dapat ditentukan; nilai ini dikembalikan ketika anotasi tidak memberikan nilai ke elemen.
Kode 4 digunakan ToDo
untuk membubuhi keterangan metode kelas yang belum selesai.
Daftar 4:AnnDemo.java
(versi 2)
public class AnnDemo {public static void main (String [] args) {String [] cities = {"New York", "Melbourne", "Beijing", "Moscow", "Paris", "London"}; urutkan (kota); } @ToDo (id = 1000, finishDate = "10/10/2019", coder = "John Doe") sortir kosong statis (Object [] objek) {}}
Kode 4 menetapkan item metadata untuk setiap elemen; misalnya, 1000
ditugaskan ke id
. Tidak seperti coder
, elemen id
dan finishDate
harus ditentukan; jika tidak, kompilator akan melaporkan kesalahan. Ketika coder
tidak diberi nilai, itu mengasumsikan "n/a"
nilai defaultnya .
Java menyediakan String value()
elemen khusus yang dapat digunakan untuk mengembalikan daftar item metadata yang dipisahkan koma. Kode 5 mendemonstrasikan elemen ini dalam versi refactored ToDo
.
Kode 5:ToDo.java
(versi 2)
public @interface ToDo {String value (); }
When value()
adalah satu-satunya elemen jenis anotasi, Anda tidak perlu menentukan value
dan =
operator penugasan saat menetapkan string ke elemen ini. Daftar 6 menunjukkan kedua pendekatan tersebut.
Kode 6:AnnDemo.java
(versi 3)
public class AnnDemo {public static void main (String [] args) {String [] cities = {"New York", "Melbourne", "Beijing", "Moscow", "Paris", "London"}; urutkan (kota); } @ToDo (value = "1000,10 / 10/2019, John Doe") static void sort (Object [] objek) {} @ToDo ("1000,10 / 10/2019, John Doe") penelusuran boolean statis ( Objek [] objek, kunci Objek) {return false; }}
Menggunakan jenis meta-annotation - masalah fleksibilitas
Anda dapat membuat anotasi tipe (mis., Kelas), metode, variabel lokal, dan banyak lagi. Namun, fleksibilitas ini bisa menimbulkan masalah. Misalnya, Anda mungkin ingin membatasi ToDo
ke metode saja, tetapi tidak ada yang mencegahnya digunakan untuk membuat anotasi elemen aplikasi lain, seperti yang ditunjukkan dalam Listing 7.
Kode 7:AnnDemo.java
(versi 4)
@ToDo ("1000,10 / 10/2019, John Doe") public class AnnDemo {public static void main (String [] args) {@ToDo (value = "1000,10 / 10/2019, John Doe") String [] kota = {"New York", "Melbourne", "Beijing", "Moskow", "Paris", "London"}; urutkan (kota); } @ToDo (value = "1000,10 / 10/2019, John Doe") static void sort (Object [] objek) {} @ToDo ("1000,10 / 10/2019, John Doe") penelusuran boolean statis ( Objek [] objek, kunci Objek) {return false; }}
Dalam Kode 7, ToDo
juga digunakan untuk membuat anotasi AnnDemo
kelas dan cities
variabel lokal. Kehadiran anotasi yang salah ini mungkin membingungkan seseorang yang sedang meninjau kode Anda, atau bahkan alat pemrosesan anotasi Anda sendiri. Saat Anda perlu mempersempit fleksibilitas jenis anotasi, Java menawarkan Target
jenis anotasi dalam java.lang.annotation
paketnya.
Target
adalah jenis anotasi meta - jenis anotasi yang anotasinya menganotasi jenis anotasi, berbeda dengan jenis anotasi non-meta yang anotasinya menganotasi elemen aplikasi, seperti kelas dan metode. Ini mengidentifikasi jenis elemen aplikasi yang dapat diterapkan jenis anotasi. Unsur-unsur ini diidentifikasi 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 associatedjava.lang.annotation.RetentionPolicy
enum declares constantsCLASS
(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), andSOURCE
(compiler discards annotations).Documented
indicates that instances ofDocumented
-annotated annotations are to be documented byjavadoc
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 apt
alat untuk memproses anotasi secara umum. Java 6 memigrasikan apt
fungsionalitasnya ke dalam javac
alat kompilernya, dan Java 7 tidak digunakan lagi apt
, yang kemudian dihapus (dimulai dengan Java 8).
Jenis anotasi standar
Seiring dengan Target
, Retention
, Documented
, dan Inherited
, Java 5 memperkenalkan java.lang.Deprecated
, java.lang.Override
dan java.lang.SuppressWarnings
. Ketiga jenis anotasi ini dirancang untuk digunakan dalam konteks compiler saja, itulah alasan kebijakan retensinya disetel ke SOURCE
.