Java Annotation cung cấp thông tin về mã nguồn, nhưng bản thân chúng không có tác động trực tiếp lên đoạn mã được chú thích.
Trong bài hướng dẫn về annotation trong Java này, chúng ta sẽ tìm hiểu các nội dung sau:
- Các annotation tích hợp sẵn trong Java
- Cách tạo annotation tùy chỉnh
- Cách sử dụng annotation và cách phân tích chúng bằng Reflection API
Java Custom Annotation
Tạo annotation tùy chỉnh trong Java khá giống với việc viết một interface, điểm khác biệt là từ khóa interface
được đặt sau ký hiệu @.
Trong annotation, ta có thể khai báo các phương thức. Hãy cùng xem ví dụ về custom annotation trong Java, sau đó chúng ta sẽ tìm hiểu các đặc điểm và những điểm quan trọng liên quan.
package com.journaldev.annotations;
import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Inherited;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Documented
@Target(ElementType.METHOD)
@Inherited
@Retention(RetentionPolicy.RUNTIME)
public @interface MethodInfo{
String author() default "Pankaj";
String date();
int revision() default 1;
String comments();
}
Một số điểm quan trọng về Java Annotation
- Các phương thức trong annotation không thể có tham số.
- Kiểu giá trị trả về của các phương thức annotation bị giới hạn trong: kiểu nguyên thủy (primitive), String, Enum, Annotation, hoặc mảng của các kiểu này.
- Các phương thức trong annotation có thể có giá trị mặc định.
- Annotation có thể được gắn với meta-annotation. Meta-annotation được dùng để cung cấp thông tin về chính annotation đó.
Meta-annotations trong Java
Có năm loại meta-annotation trong Java:
- @TargetXác định loại phần tử chương trình mà annotation có thể áp dụng. Một số giá trị phổ biến gồm:
TYPE
,METHOD
,CONSTRUCTOR
,FIELD
… Nếu không khai báo@Target
, annotation có thể được dùng trên bất kỳ phần tử nào. - @Inherited Chỉ ra rằng một annotation type sẽ được tự động kế thừa. Nếu người dùng truy vấn annotation trên một class mà class đó không khai báo annotation tương ứng, thì hệ thống sẽ tự động tìm annotation này trong superclass của class đó. Quá trình này sẽ tiếp tục lặp lại cho đến khi tìm thấy annotation phù hợp hoặc đạt đến đỉnh của hệ thống phân cấp lớp (tức là class
Object
). - @RetentionXác định thời điểm giữ lại annotation. Annotation này sử dụng đối số
RetentionPolicy
với các giá trị có thể là:SOURCE
: Chỉ tồn tại trong mã nguồn, bị loại bỏ khi biên dịchCLASS
: Tồn tại trong file.class
, nhưng không còn ở runtimeRUNTIME
: Tồn tại đến lúc chương trình chạy, có thể được đọc qua Reflection API
- @RepeatableĐược sử dụng để khai báo một annotation type có thể lặp lại, tức là có thể khai báo nhiều lần trên cùng một phần tử chương trình.
Annotation có sẵn trong Java
Java cung cấp năm annotation tích hợp sẵn phổ biến:
- @OverrideDùng để chú thích rằng một phương thức đang ghi đè từ class cha. Nếu method ở class cha bị đổi tên hoặc xóa, compiler sẽ báo lỗi. Đây là annotation nên dùng mỗi khi override một method.
- @DeprecatedĐánh dấu một method, class hoặc field là đã lỗi thời (deprecated). Trong Javadoc, nên cung cấp lý do deprecated và phương án thay thế.
- @SuppressWarningsDùng để bỏ qua cảnh báo từ compiler, ví dụ như khi dùng raw type trong Java Generics. Annotation này có retention policy là SOURCE và sẽ bị loại bỏ sau khi biên dịch.
- @FunctionalInterfaceGiới thiệu từ Java 8, annotation này chỉ ra rằng interface đó được thiết kế để trở thành một functional interface (tức là chỉ có duy nhất một abstract method).
- @SafeVarargsDùng để khẳng định rằng nội dung method hoặc constructor không thực hiện các thao tác nguy hiểm với đối số dạng varargs.
Ví dụ về Annotation trong Java
Sau đây là một ví dụ trong Java sử dụng kết hợp cả annotation có sẵn và annotation tùy chỉnh mà ta đã tạo ở phần trên.
package com.journaldev.annotations;
import java.io.FileNotFoundException;
import java.util.ArrayList;
import java.util.List;
public class AnnotationExample {
public static void main(String[] args) {
}
@Override
@MethodInfo(author = "Pankaj", comments = "Main method", date = "Nov 17 2012", revision = 1)
public String toString() {
return "Overriden toString method";
}
@Deprecated
@MethodInfo(comments = "deprecated method", date = "Nov 17 2012")
public static void oldMethod() {
System.out.println("old method, don't use it.");
}
@SuppressWarnings({ "unchecked", "deprecation" })
@MethodInfo(author = "Pankaj", comments = "Main method", date = "Nov 17 2012", revision = 10)
public static void genericsTest() throws FileNotFoundException {
List l = new ArrayList();
l.add("abc");
oldMethod();
}
}
Tôi tin rằng ví dụ về Java annotation ở trên đã tự minh họa đầy đủ cách sử dụng annotation trong các tình huống khác nhau.
Phân tích Java Annotation
Chúng ta sẽ sử dụng Reflection để phân tích annotation trong một class Java.
Lưu ý quan trọng:
Annotation cần được khai báo với Retention Policy là RUNTIME
, nếu không thì thông tin về annotation sẽ không tồn tại tại thời điểm chạy, và chúng ta sẽ không thể truy xuất bất kỳ dữ liệu nào từ annotation đó.
package com.journaldev.annotations;
import java.lang.annotation.Annotation;
import java.lang.reflect.Method;
public class AnnotationParsing {
public static void main(String[] args) {
try {
for (Method method : AnnotationParsing.class.getClassLoader()
.loadClass(("com.journaldev.annotations.AnnotationExample")).getMethods()) {
// checks if MethodInfo annotation is present for the method
if (method.isAnnotationPresent(com.journaldev.annotations.MethodInfo.class)) {
try {
// iterates all the annotations available in the method
for (Annotation anno : method.getDeclaredAnnotations()) {
System.out.println("Annotation in Method '" + method + "' : " + anno);
}
MethodInfo methodAnno = method.getAnnotation(MethodInfo.class);
if (methodAnno.revision() == 1) {
System.out.println("Method with revision no 1 = " + method);
}
} catch (Throwable ex) {
ex.printStackTrace();
}
}
}
} catch (SecurityException | ClassNotFoundException e) {
e.printStackTrace();
}
}
}
Kết quả đầu ra của chương trình trên là:
Annotation in Method 'public java.lang.String com.journaldev.annotations.AnnotationExample.toString()' : @com.journaldev.annotations.MethodInfo(author=Pankaj, revision=1, comments=Main method, date=Nov 17 2012)
Method with revision no 1 = public java.lang.String com.journaldev.annotations.AnnotationExample.toString()
Annotation in Method 'public static void com.journaldev.annotations.AnnotationExample.oldMethod()' : @java.lang.Deprecated()
Annotation in Method 'public static void com.journaldev.annotations.AnnotationExample.oldMethod()' : @com.journaldev.annotations.MethodInfo(author=Pankaj, revision=1, comments=deprecated method, date=Nov 17 2012)
Method with revision no 1 = public static void com.journaldev.annotations.AnnotationExample.oldMethod()
Annotation in Method 'public static void com.journaldev.annotations.AnnotationExample.genericsTest() throws java.io.FileNotFoundException' : @com.journaldev.annotations.MethodInfo(author=Pankaj, revision=10, comments=Main method, date=Nov 17 2012)