Hướng dẫn sử dụng Java Reflection

1. Java Reflection là gì?

Java là một ngôn ngữ hướng đối tượng (Object-oriented), thông thường bạn cần tạo ra một đối tượng và bạn có thể truy cập vào các trường (field), hoặc gọi phương thức (method) của đối tượng này thông qua toán tử dấu chấm ( . ).

Java Reflection giới thiệu một cách tiếp cận khác, bạn có thể truy cập vào một trường của một đối tượng nếu bạn biết tên của trường đó. Hoặc bạn có thể gọi một phương thức của đối tượng nếu bạn biết tên phương thức, các kiểu tham số của phương thức, và các giá trị tham số để truyền vào …

Java Reflecion cho phép bạn truy cập, sửa đổi cấu trúc và hành vi của một đối tượng tại thời gian chạy (runtime) của chương trình. Đồng thời nó cho phép bạn truy cập vào các thành viên private (private member) tại mọi nơi trong ứng dụng, điều này không được phép với cách tiếp cận truyền thống.

Java Reflection khá mạnh mẽ và rất hữu ích đối với những ai hiểu rõ về nó. Ví dụ, bạn có thể ánh xạ (mapping) đối tượng (object) thành table dưới database tại thời điểm runtime. Kỹ thuật này các bạn có thể thấy rõ nhất ở JPA và Hibernate.

2. Một số class được sử dụng trong bài viết này

Animal.java

package com.maixuanviet.reflection;
 
public abstract class Animal {
 
    public String getLocation() {
        return "VietNam";
    }
 
    public abstract int getNumberOfLegs();
 
}

Say.java

package com.maixuanviet.reflection;
 
public interface Say {
 
    public String say();
 
}

Cat.java

package com.maixuanviet.reflection;
 
@Excel(name = "Cat")
public class Cat extends Animal implements Say {
 
    // Public fields
    public static final String SAY = "Meo meo";
    public static final int NUMBER_OF_LEGS = 4;
 
    // Private fields
    @ExcelColumn(index = 0, title = "Name")
    private String name;
 
    @ExcelColumn(index = 1, title = "Age")
    public int age;
 
    // Construnctors
    public Cat() {
 
    }
 
    public Cat(String name) {
        this.name = name;
        this.age = 1;
    }
 
    public Cat(String name, int age) {
        this.name = name;
        this.age = age;
    }
 
    /**
     * Implements from interface Say.
     */
    @Override
    public String say() {
        return SAY;
    }
 
    /**
     * Implements from Animal.
     */
    @Override
    public int getNumberOfLegs() {
        return NUMBER_OF_LEGS;
    }
 
    // Private Method.
    private void setName(String name) {
        this.name = name;
    }
 
    // Getters and setters
 
    public String getName() {
        return this.name;
    }
 
    public int getAge() {
        return this.age;
    }
 
    public void setAge(int age) {
        this.age = age;
    }
 
}

Excel.java

package com.maixuanviet.reflection;
 
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.TYPE)
@Inherited
@Retention(RetentionPolicy.RUNTIME)
public @interface Excel {
 
    int index() default 0;
 
    String name() default "Sheet 1";
 
}

ExcelColumn.java

package com.maixuanviet.reflection;
 
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.FIELD)
@Inherited
@Retention(RetentionPolicy.RUNTIME)
public @interface ExcelColumn {
 
    int index();
 
    String title();
 
    String description() default "Default value";
 
}

3. Kiến trúc của Java Reflection API

Các lớp được dùng trong reflection nằm trong hai package là java.lang và java.lang.reflect. Package java.lang.reflect bao gồm ba lớp chính mà bạn cần biết là Constructor, Field và Method:

  • Class<T>: lớp này đại diện cho các lớp, interface và chứa các phương thức dùng để lấy các đối tượng kiểu Constructor, Field, Method,…
  • AccessibleObject: các kiểm tra về phạm vi truy xuất (public, private, protected) của field, method, constructor sẽ được bỏ qua. Nhờ đó bạn có thể dùng reflection để thay đổi, thực thi các thành phần này mà không cần quan tâm đến phạm vi truy xuất của nó.
  • Constructor: chứa các thông tin về một constructor của lớp.
  • Field: chứa các thông tin về một field của lớp, interface.
  • Method: chứa các thông tin về một phương thức của lớp, interface.

3.1. Lớp (Classes)

Khi sử dụng Java Reflection để duyệt qua một class thì việc đầu tiên thường phải làm đó là có được một đối tượng kiểu Class, từ các đối tượng kiểu Class chúng ta có thể lấy được các thông tin về:

  • Class Name
  • Class Modifies (public, private, synchronized etc.)
  • Package Info
  • Superclass
  • Implemented Interfaces
  • Constructors
  • Methods
  • Fields
  • Annotations

3.2. Tạo đối tượng Class<>

Đối tượng kiểu Class được tạo ra bằng cách sử dụng phương thức static Class.forName(). Cách này thường được sử dụng khi chỉ biên được tên lớp lúc thực thi (runtime):

try {
  
    Class c =  Class.forName("com.maixuanviet.Cat");
    // ...
} catch (ClassNotFoundException e) {
    System.err.println(e);
}

Trong trường hợp không tìm thấy lớp tương ứng, phương thức trên sẽ ném ra ngoại lệ ClassNotFoundException. Điều này có thể bất tiện vì bạn phải sử dụng try catch hoặc ném ngoại lệ này khỏi phương thức.

Khi bạn biết chính xác tên Lớp tại thời điểm biên dịch (combine), có thể sử dụng TenLop.class để tạo đối tượng kiểu Class. Cách này đảm bảo rằng lớp được sử dụng luôn luôn tồn tại và không có ngoại lệ nào xảy ra. Đối với các kiểu dữ liệu nguyên thủy như void, int, boolean, char,… bạn có thể dùng field TYPE để lấy được đối tượng Class tương ứng.

Class c1 = Cat.class;
Class c2 = int.class;
Class c3 = Integer.class;
Class c4 = Integer.TYPE;

Nếu bạn có một đối tượng, bạng cũng có thể lấy được đối tượng Class.

Cat cat = new Cat();
Class c = cat.getClass();

Sau đây là một ví dụ đơn giản dùng reflection để in ra các thông tin của lớp:

package com.maixuanviet.reflection;
 
import java.lang.annotation.Annotation;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
 
public class ReflectClassExample {
 
    public static void main(String[] args) {
        try {
            getClassInfo();
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        }
    }
 
    public static void getClassInfo() throws ClassNotFoundException {
        Class<?> aClazz = Class.forName("com.maixuanviet.reflection.Cat");
        System.out.println("Name: " + aClazz.getName());
        System.out.println("Simple Name: " + aClazz.getSimpleName());
 
        Package pkg = aClazz.getPackage();
        System.out.println("Package Name = " + pkg.getName());
 
        // Modifier
        int modifiers = aClazz.getModifiers();
        boolean isPublic = Modifier.isPublic(modifiers);
        boolean isInterface = Modifier.isInterface(modifiers);
        boolean isAbstract = Modifier.isAbstract(modifiers);
        boolean isFinal = Modifier.isFinal(modifiers);
 
        System.out.println("Is Public? " + isPublic); // true
        System.out.println("Is Final? " + isFinal); // false
        System.out.println("Is Interface? " + isInterface); // false
        System.out.println("Is Abstract? " + isAbstract); // false
 
        // Lấy ra đối tượng class mô tả class cha của class Cat.
        Class<?> aSuperClass = aClazz.getSuperclass();
        System.out.println("Simple Class Name of Super class = " + aSuperClass.getSimpleName());
 
        // Lấy ra mảng các Class mô tả các Interface mà Cat thi hành
        System.out.println("\nInterface:");
        Class<?>[] itfClasses = aClazz.getInterfaces();
        for (Class<?> itfClass : itfClasses) {
            System.out.println("+ " + itfClass.getSimpleName());
        }
 
        // Lấy ra danh sách các cấu tử của Cat.
        System.out.println("\nConstructor:");
        Constructor<?>[] constructors = aClazz.getConstructors();
        for (Constructor<?> constructor : constructors) {
            System.out.println("+ " + constructor.getName() + " has " + constructor.getParameterCount() + " param");
        }
 
        // Lấy ra danh sách các method public của Cat
        // Bao gồm cả các method thừa kế từ class cha và các interface
        System.out.println("\nDeclared Methods:");
        Method[] methods = aClazz.getDeclaredMethods();
        for (Method method : methods) {
            System.out.println("+ " + method.getName());
        }
 
        // Lấy ra danh sách các field public
        // Kể các các public field thừa kế từ các class cha, và các interface
        System.out.println("\nField:");
        Field[] fields = aClazz.getFields();
        for (Field field : fields) {
            System.out.println("+ " + field.getName());
        }
 
        // Lấy ra danh sách các Annotation của class.
        System.out.println("\nAnnotation:");
        Annotation[] annotations = aClazz.getAnnotations();
        for (Annotation ann : annotations) {
            System.out.println("+ " + ann.annotationType().getSimpleName());
        }
    }
}

Kết quả thực thi chương trình trên:

Name: com.maixuanviet.reflection.Cat
Simple Name: Cat
Package Name = com.maixuanviet.reflection
Is Public? true
Is Final? false
Is Interface? false
Is Abstract? false
Simple Class Name of Super class = Animal
 
Interface:
+ Say
 
Constructor:
+ com.maixuanviet.reflection.Cat has 2 param
+ com.maixuanviet.reflection.Cat has 1 param
+ com.maixuanviet.reflection.Cat has 0 param
 
Declared Methods:
+ getName
+ setName
+ say
+ getNumberOfLegs
+ setAge
+ getAge
 
Field:
+ SAY
+ NUMBER_OF_LEGS
+ age
 
Annotation:
+ Excel

3.3. Cấu tử (Constructor)

3.3.1. Lấy tất cả Constructor của một Class

Các đối tượng lớp Contructor là những phuơng thức khởi tạo của một lớp. Reflection cho phép lấy ra những Contructor từ Class Object:

Class aClazz = Cat.class; // obtain class object
Constructor[] constructors = aClazz.getConstructors();

3.3.2. Lấy một Constructor cụ thể

Nếu như bạn biết chính xác các kiểu parameter của constructor mà bạn muốn access đến thì bạn có thể lấy về đối tượng Constructor mà mình mong muốn thay vì phải lấy tất cả (một mảng).

Class aClazz = Cat.class; // obtain Class object
Constructor constructor = aClazz.getConstructor(new Class[]{String.class});

Lưu ý: Khi bạn thực hiện lấy 1 đối tượng Constructor của một lớp bất kỳ, nhưng Constructor không tồn tại thì nó sẽ quăng ra NoSuchMethodException.

3.3.3. Lấy danh sách tham số của một Constructor

Bạn cũng có thể truy cập được đến tham số của các Contructor, các tham số này đều được đua về kiểu Class.

Constructor constructor = ...; // obtain constructor
Class[] parameterTypes = constructor.getParameterTypes();

3.3.4. Khởi tạo đối tượng từ đối tượng Constructor

Có hai phương thức để tạo một thể hiện của lớp:

  • Class.newInstance(): tạo một đối tượng với constructor không có tham số.
  • Constructor.newInstance(Object[] initargs): tạo đối tượng với constructor có tham số.
// Lấy đối tượng constructor của Cat class không có tham số
Class aClazz = Cat.class; // obtain Class object
Cat cat1 = (Cat) aClazz.newInstance();
 
// Lấy đối tượng constructor của Cat class với argument là kiểu String
Constructor constructor = Cat.class.getConstructor(String.class);
Cat cat2 = (Cat) constructor.newInstance("Tom");

3.3.5. Ví dụ

package com.maixuanviet.reflection;
 
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
 
public class ReflectConstructorExample {
    public static void main(String[] args) throws NoSuchMethodException, SecurityException, InstantiationException,
            IllegalAccessException, IllegalArgumentException, InvocationTargetException {
 
        // Lấy ra đối tượng Class mô tả class Cat
        Class<Cat> aClass = Cat.class;
 
        // Lấy ra cấu tử có tham số (String,int) của class Cat
        Constructor<?> constructor = aClass.getConstructor(String.class, int.class);
 
        // Lấy ra thông tin kiểu tham số của cấu tử.
        System.out.println("Params:");
        Class<?>[] paramClasses = constructor.getParameterTypes();
        for (Class<?> paramClass : paramClasses) {
            System.out.println("+ " + paramClass.getSimpleName());
        }
 
        // Khởi tạo đối tượng Cat theo cách thông thường.
        Cat tom = new Cat("Tom", 1);
        System.out.println("Cat 1: " + tom.getName() + ", age =" + tom.getAge());
 
        // Khởi tạo đối tượng Cat theo cách của reflect.
        Cat tom2 = (Cat) constructor.newInstance("Tom", 2);
        System.out.println("Cat 2: " + tom.getName() + ", age =" + tom2.getAge());
    }
}

Kết quả thực thi chương trình trên:

Params:
+ String
+ int
Cat 1: Tom, age =1
Cat 2: Tom, age =2

Lưu ý:

Bởi vì Class là một lớp Generic, bạn có thể dùng kí tự wildcard để xác định kiểu dữ liệu mà nó đại diện:

Class<?> c1 = int.class;
Class<? extends Number> c2 = int.class;

3.4. Trường (Field)

3.4.1. Lấy các đối tượng field được khai báo là public

Bạn có thể lấy được đối tượng field được khai báo là public của một Class bằng 2 cách là chỉ lấy một field duy nhất nếu bạn biết chính xác tên của 1 field, hoặc lấy nguyên 1 mảng danh sách các field của từ một đối tượng Class.

Class aClazz = Cat.class;
Field field = aClazz.getField("name"); // Tên field cần lấy
 
// Lấy danh sách tất cả các field được khai báo là public
Field[] fields = aClazz.getFields();

Lưu ý: Phương thức getField() sẽ ném ra NoSuchFieldException nếu như không tồn tại Field với tên bạn đưa vào.

3.4.2. Lấy các đối tượng field khai báo bất kỳ

Phương thức getField() và getFields() chỉ có thể lấy các field được khai báo là public. Vậy làm sao để access được những field được khai báo là private, protected,…? Khá đơn giản, trong Java bạn có thể lấy được chúng thông qua 2 methods là getDeclaredField() và getDeclaredFields() như sau:

Class aClazz = Cat.class;
Field field = aClazz.getDeclaredField("name"); // Tên field cần lấy
 
// Lấy danh sách tất cả các field được khai báo là public, private, ...
Field[] fields = aClazz.getDeclaredFields();

Lưu ý: Các Exception có thể được ném ra khi gọi phương thức getDeclaredField():

  • NoSuchFieldException : Nếu tên field bạn nhập vào không tìm thấy trong Class Cat.
  • NullPointerException : Nếu tham số truyền vào là null.
  • SecurityException : Nếu có Security manager được áp dụng ở lớp này.

3.4.3. Lấy tên field, kiểu dữ liệu kiểu field

Sau khi bạn đã có được đối tượng Field hoặc mảng Field[] bạn muốn biết tên của Field chỉ cần gọi phương thức getName() để lấy tên field, hoặc getType() để lấy kiểu dữ liệu của field.

Class<?> aClazz = Class.forName("com.gpcoder.reflection.Cat");
Field field = aClazz.getDeclaredField("name"); // Tên field cần lấy
String fieldName = field.getName(); // Lấy tên field
Class<?> type = field.getType(); // Lấy kiểu dữ liệu của field

3.4.4. Gán giá trị cho Field

Gọi phương thức Field.set(Object obj, Object value): gán value cho field tương ứng của đối tượng obj.

3.4.5. Ví dụ

package com.maixuanviet.reflection;
 
import java.lang.annotation.Annotation;
import java.lang.reflect.Field;
 
public class ReflectFieldExample {
 
    public static void main(String[] args) throws ClassNotFoundException, NoSuchFieldException, SecurityException,
            IllegalArgumentException, IllegalAccessException {
        // Lấy ra đối tượng Class mô tả class Cat
        Class<Cat> aClazz = Cat.class;
 
        // Lấy ra danh sách các field public
        // Kể các các public field thừa kế từ các class cha, và các interface
        System.out.println("Field:");
        Field[] fields = aClazz.getFields();
        for (Field field : fields) {
            System.out.println("+ " + field.getName());
        }
 
        // Lấy ra field có tên 'NUMBER_OF_LEGS':
        Field field = aClazz.getField("NUMBER_OF_LEGS");
 
        // Ghi ra kiểu của Field
        Class<?> fieldType = field.getType();
        System.out.println("Field type: " + fieldType.getSimpleName());
 
        // Khởi tạo đối tượng Cat
        Cat tom = new Cat("Tom", 1);
 
        // Lấy ra giá trị của trường "age" theo cách của Reflect.
        Field ageField = aClazz.getField("age");
        Integer age = (Integer) ageField.get(tom);
        System.out.println("Age = " + age);
 
        // Gán giá trị mới cho trường "age".
        ageField.set(tom, 2);
        System.out.println("New Age = " + tom.getAge());
 
        // Lấy ra danh sách các Annotation của field.
        System.out.println("\nAnnotation:");
        Annotation[] annotations = ageField.getAnnotations();
        for (Annotation ann : annotations) {
            System.out.println("+ " + ann.annotationType().getSimpleName());
        }
    }
 
}

Kết quả thực thi chương trình trên:

Field:
+ SAY
+ NUMBER_OF_LEGS
+ age
Field type: int
Age = 1
New Age = 2
 
Annotation:
+ ExcelColumn

3.5. Phương thức (method)

Như đã nói ở trước đó, khi bạn sử dụng java reflection bạn có thể truy cập vào các phuơng thức của một lớp và gọi những phuơng thức này.

3.5.1. Lấy tất cả Method của một Class

Phương thức Class.getMethods(): trả về danh sách đối tượng Method của một lớp.

Class aClazz = Cat.class; // obtain class object
Method[] methods = aClazz.getMethods();

3.5.2. Lấy một Method cụ thể

Phương thức Class.getMethod(String name, Class[] parameterTypes): trả về đối tượng Method đại diện cho một phương thức của lớp. Phương thức này được xác định qua tên và các kiểu tham số.

Class aClazz = Cat.class; // obtain class object
Method method = aClazz.getMethod("setName", String.class); // get method that takes a String as argument

3.5.3. Thực thi một Method

Phương thức Method.invoke(Object obj, Object[] args) thực thi phương thức tương ứng của đối tượng obj với các tham số args.

3.5.4. Ví dụ

Ví dụ lấy ra một method cho bởi tên, và các tham số chỉ định trước. Ghi ra thông tin về method này, như kiểu trả về, danh sách các tham số,…

package com.maixuanviet.reflection;
 
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
 
public class ReflectMethodExample {
 
    public static void main(String[] args) throws NoSuchMethodException, SecurityException, IllegalAccessException,
            IllegalArgumentException, InvocationTargetException {
 
        // Lấy ra đối tượng Class mô tả class Cat
        Class<Cat> aClazz = Cat.class;
 
        // Lấy ra danh sách các method public của Cat
        // Bao gồm cả các method thừa kế từ class cha và các interface
        System.out.println("Declared Methods:");
        Method[] methods = aClazz.getDeclaredMethods();
        for (Method method : methods) {
            System.out.println("+ " + method.getName());
        }
 
        // Lấy ra đối tượng 'Method' mô tả method getAge()
        Method getAgeMethod = aClazz.getMethod("getAge");
 
        // Kiểu trả về của method getAge
        Class<?> returnType = getAgeMethod.getReturnType();
        System.out.println("Return type of getAge: " + returnType.getSimpleName());
 
        Cat tom = new Cat("Tom", 1);
 
        // Gọi method 'getAge' theo cách của Reflect
        // Nó tương đương với gọi: tom.getAge()
        int age = (int) getAgeMethod.invoke(tom);
 
        System.out.println("Age = " + age);
 
        // Lấy ra đối tượng 'Method' mô tả method setAge(int) của class Cat.
        Method setAgeMethod = aClazz.getMethod("setAge", int.class);
 
        // Gọi method setAge(int) theo cách của Reflect.
        // Nó tương đương với gọi: tom.setAge(2);
        setAgeMethod.invoke(tom, 2);
 
        System.out.println("New Age = " + tom.getAge());
    }
 
}

Kết quả thực thi chương trình trên:

Declared Methods:
+ getName
+ setName
+ getNumberOfLegs
+ setAge
+ getAge
+ say
Return type of getAge: int
Age = 1
New Age = 2

Ví dụ dưới đây, liệt kê ra các phương thức public setter, và các public getter của class.

package com.maixuanviet.reflection;
 
import java.lang.reflect.Method;
 
public class ReflectMethodExample2 {
 
    public static void main(String[] args) {
 
        // Lấy ra đối tượng Class mô tả class Cat
        Class<Cat> aClazz = Cat.class;
 
        // Lấy ra danh sách các public method.
        Method[] methods = aClazz.getMethods();
 
        for (Method method : methods) {
            System.out.println("Method: " + method.getName());
            System.out.println(" - Is Setter? " + isSetter(method));
            System.out.println(" - Is Getter? " + isGetter(method));
        }
 
    }
 
    // Method là getter nếu có tên bắt đầu bằng get, và không có tham số.
    public static boolean isGetter(Method method) {
        if (!method.getName().startsWith("get")) {
            return false;
        }
        if (method.getParameterTypes().length != 0) {
            return false;
        }
        if (void.class.equals(method.getReturnType())) {
            return false;
        }
        return true;
    }
 
    // Method là setter nếu có tên bắt đầu bằng set, và chỉ có 1 tham số.
    public static boolean isSetter(Method method) {
        if (!method.getName().startsWith("set")) {
            return false;
        }
        if (method.getParameterTypes().length != 1) {
            return false;
        }
        return true;
    }
 
}

Kết quả thực thi chương trình trên:

Method: getName
 - Is Setter? false
 - Is Getter? true
Method: getNumberOfLegs
 - Is Setter? false
 - Is Getter? true
Method: setAge
 - Is Setter? true
 - Is Getter? false
Method: say
 - Is Setter? false
 - Is Getter? false
Method: getAge
 - Is Setter? false
 - Is Getter? true
Method: getLocation
 - Is Setter? false
 - Is Getter? true
Method: wait
 - Is Setter? false
 - Is Getter? false
Method: wait
 - Is Setter? false
 - Is Getter? false
Method: wait
 - Is Setter? false
 - Is Getter? false
Method: equals
 - Is Setter? false
 - Is Getter? false
Method: toString
 - Is Setter? false
 - Is Getter? false
Method: hashCode
 - Is Setter? false
 - Is Getter? false
Method: getClass
 - Is Setter? false
 - Is Getter? true
Method: notify
 - Is Setter? false
 - Is Getter? false
Method: notifyAll
 - Is Setter? false
 - Is Getter? false

3.6. Truy cập vào các private method, field

Bạn không thể truy cập vào các method hay field mà nó là private theo cách thông thường, quá trình biên dịch java cũng không cho phép điều đó. Nhưng với Java Reflection điều đó hoàn toàn có thể.

Ví dụ truy cập vào một private field.

package com.maixuanviet.reflection;
 
import java.lang.reflect.Field;
 
public class AccessPrivateFieldExample {
    public static void main(String[] args)
            throws IllegalArgumentException, IllegalAccessException, NoSuchFieldException, SecurityException {
 
        // Tạo một đối tượng Class mô tả class Cat.
        Class<Cat> aClazz = Cat.class;
 
        // Class.getField(String) chỉ lấy được các trường public.
        // Sử dụng Class.getDeclaredField(String):
        // Lấy ra đối tượng Field mô tả trường name của class Cat.
        // (Trường khi báo trong class này).
        Field private_nameField = aClazz.getDeclaredField("name");
 
        // Cho phép để truy cập vào các trường private.
        // Nếu không sẽ bị ngoại lệ IllegalAccessException
        private_nameField.setAccessible(true);
 
        Cat tom = new Cat("Tom");
 
        String fieldValue = (String) private_nameField.get(tom);
        System.out.println("Value field name = " + fieldValue);
 
        // Sét đặt trường name giá trị mới.
        private_nameField.set(tom, "Tom Cat");
 
        System.out.println("New name = " + tom.getName());
    }
}

Kết quả thực thi chương trình trên:

Value field name = Tom
New name = Tom Cat

Ví dụ truy cập vào một private method.

package com.maixuanviet.reflection;
 
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
 
public class AccessPrivateMethodExample {
    public static void main(String[] args) throws NoSuchMethodException, SecurityException, IllegalAccessException,
            IllegalArgumentException, InvocationTargetException {
 
        // Tạo một đối tượng Class mô tả class Cat.
        Class<Cat> aClass = Cat.class;
 
        // Class.getMethod(String) chỉ lấy được các method public.
        // Sử dụng Class.getDeclaredMethod(String):
        // Lấy ra đối tượng Method mô tả method setName(String) của class Cat.
        // (Phương thức khai báo trong class).
        Method private_setNameMethod = aClass.getDeclaredMethod("setName", String.class);
 
        // Cho phép để truy cập vào các method private.
        // Nếu không sẽ bị ngoại lệ IllegalAccessException
        private_setNameMethod.setAccessible(true);
 
        Cat tom = new Cat("Tom");
 
        // Gọi private method.
        private_setNameMethod.invoke(tom, "Tom Cat");
 
        System.out.println("New name = " + tom.getName());
    }
}

Kết quả thực thi chương trình trên:

New name = Tom Cat

3.7. Thay đổi giá trị field có khai báo là static final

package com.maixuanviet.reflection;
 
import java.lang.reflect.Field;
import java.lang.reflect.Modifier;
 
public class ChangeStaticFinalFieldExample {
    public static void main(String[] args)
            throws NoSuchFieldException, SecurityException, IllegalArgumentException, IllegalAccessException {
        // create Cat
        Cat cat = new Cat();
         
        // Get field instance
        Field field = cat.getClass().getDeclaredField("NUMBER_OF_LEGS");
        field.setAccessible(true); // Suppress Java language access checking
 
        // Remove "final" modifier
        Field modifiersField = Field.class.getDeclaredField("modifiers");
        modifiersField.setAccessible(true);
        modifiersField.setInt(field, field.getModifiers() & ~Modifier.FINAL);
 
        // Get value
        Integer fieldValue = (Integer) field.get(null);
        System.out.println(cat.getNumberOfLegs()); // -> 4
 
        // Set value
        field.set(null, 2);
        System.out.println(cat.getNumberOfLegs()); // -> 2
    }
}

3.8. Chú thích (Annotation)

Bạn có thể sử dụng Reflection để truy cập (access) các Annotations được áp dụng trong các class tại thời điểm Runtime.

Ví dụ tạo một Annnotation như sau:

package com.maixuanviet.reflection;
 
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
 
// Annotation này có thể sử dụng tại thời điểm chạy (Runtime) của chương trình.
@Retention(RetentionPolicy.RUNTIME)
// Có thể dùng cho class,interface, method, field, parameter.
@Target({ ElementType.TYPE, ElementType.METHOD, ElementType.FIELD, ElementType.PARAMETER })
public @interface MyAnnotation {
 
    String name();
 
    String value() default "Defaul value";
}

3.8.1. Một ví dụ Annotation với class

package com.maixuanviet.reflection;
 
import java.lang.annotation.Annotation;
 
@MyAnnotation(name = "Table", value = "Employee")
public class ClassAnnotationExample {
 
    public static void main(String[] args) {
 
        Class<?> aClazz = ClassAnnotationExample.class;
 
        // Lấy ra danh sách các Annotation của class.
        Annotation[] annotations = aClazz.getAnnotations();
 
        for (Annotation ann : annotations) {
            System.out.println("Annotation: " + ann.annotationType().getSimpleName());
        }
 
        // Hoặc lấy cụ thể.
        Annotation ann = aClazz.getAnnotation(MyAnnotation.class);
        MyAnnotation myAnn = (MyAnnotation) ann;
        System.out.println("Name = " + myAnn.name());
        System.out.println("Value = " + myAnn.value());
    }
}

Kết quả thực thi chương trình trên:

Annotation: MyAnnotation
Name = Table
Value = Employee

3.8.2. Ví dụ Annotation với Field và Method

package com.maixuanviet.reflection;
 
import java.lang.annotation.Annotation;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
 
public class FieldMethodAnnotationExample {
 
    @MyAnnotation(name = "My Field")
    private int myField;
 
    @MyAnnotation(name = "My Method", value = "My Method Value")
    protected void myMethod(String str) {
 
    }
 
    public static void main(String[] args) throws NoSuchFieldException, SecurityException, NoSuchMethodException {
        // Lấy đối tượng Class
        Class<?> aClazz = FieldMethodAnnotationExample.class;
 
        // Lấy đối tượng Field
        System.out.println(" == FIELD == ");
        Field field = aClazz.getDeclaredField("myField");
 
        // Lấy ra danh sách các Annotation của field.
        Annotation[] fieldAnns = field.getAnnotations();
        for (Annotation methodAnn : fieldAnns) {
            System.out.println("Annotation: " + methodAnn.annotationType().getSimpleName());
        }
 
        // Lấy cụ thể.
        Annotation fieldAnn = field.getAnnotation(MyAnnotation.class);
        MyAnnotation myAnn1 = (MyAnnotation) fieldAnn;
        System.out.println("Name = " + myAnn1.name());
        System.out.println("Value = " + myAnn1.value());
 
        // Tương tự với method ...
        System.out.println(" == METHOD == ");
        Method method = aClazz.getDeclaredMethod("myMethod", String.class);
        // Lấy ra danh sách các Annotation của method.
        Annotation[] methodAnns = method.getAnnotations();
        for (Annotation methodAnn : methodAnns) {
            System.out.println("Annotation: " + methodAnn.annotationType().getSimpleName());
        }
 
        // Lấy cụ thể.
        Annotation methodAnn = method.getAnnotation(MyAnnotation.class);
        MyAnnotation myAnn2 = (MyAnnotation) methodAnn;
        System.out.println("Name = " + myAnn2.name());
        System.out.println("Value = " + myAnn2.value());
 
    }
}

Kết quả thực thi chương trình trên:

== FIELD == 
Annotation: MyAnnotation
Name = My Field
Value = Defaul value
 == METHOD == 
Annotation: MyAnnotation
Name = My Method
Value = My Method Value

3.8.3. Ví dụ Annotation với tham số của method

package com.maixuanviet.reflection;
 
import java.lang.annotation.Annotation;
import java.lang.reflect.Method;
 
public class ParameterAnnotationExample {
 
    // Ví dụ một method có Annotation ở tham số.
    protected void doSomething(int jobType, @MyAnnotation(name = "Table", value = "Employee") String info) {
 
    }
 
    public static void main(String[] args) throws NoSuchMethodException, SecurityException {
        // Lấy đối tượng Class
        Class<?> aClass = ParameterAnnotationExample.class;
 
        // Lấy ra đối tượng Method của method doSomething(int,String)
        Method method = aClass.getDeclaredMethod("doSomething", int.class, String.class);
 
        // Lấy ra danh sách các Parameter của method.
        Class<?>[] parameterTypes = method.getParameterTypes();
        for (Class<?> parameterType : parameterTypes) {
            System.out.println("Parametete Type: " + parameterType.getSimpleName());
        }
 
        System.out.println(" ---- ");
 
        // Lấy ra mảng 2 chiều các Annotation trong các Parameter.
        Annotation[][] annotationss = method.getParameterAnnotations();
 
        // Lấy ra danh sách các Annotation của Parameter tại vị trí Index =1.
        Annotation[] annotations = annotationss[1];
        for (Annotation ann : annotations) {
            System.out.println("Annotation: " + ann.annotationType().getSimpleName());
        }
    }
}

Kết quả thực thi chương trình trên:

Parametete Type: int
Parametete Type: String
 ---- 
Annotation: MyAnnotation

2 Trackbacks / Pingbacks

  1. Kết hợp Java Reflection và Java Annotations – Blog của VietMX
  2. Làm thế nào tạo instance của một class mà không gọi từ khóa new? – Blog của VietMX

Comments are closed.