Làm quen với Java Reflection

Trong bài viết này chúng ta sẽ cùng nhau tìm hiểu về Java reflection, một kỹ thuật cho phép kiểm tra hoặc sửa đổi các thuộc tính, method trong class, interface tại thời điểm runtime. Ngoài ra chúng ta cũng có thể sử dụng reflection để khởi tạo các object, thực hiện các lời gọi hàm hoặc thậm chí có thể truy cập hoặc thay đổi giá trị của các thuộc tính trong một class.

Làm thế nào để sử dụng Java Reflection?

Để sử dụng Java reflection, chúng ta không cần thêm bất kỳ gói jars, dependency hay thực hiện các bước cấu hình phức tạp nào. JDK đã cung cấp sẵn một package java.lang.reflect chứa tất cả những gì cần thiết để sử dụng kỹ thuật này.

Để làm quen với Java reflection, chúng ta sẽ làm một ví dụ cơ bản như sau, giả sử chúng ta có một class Person gồm 2 thuộc tính name và age và không chứa bất kỳ method nào bao gồm cả getter, setter.

public class Person {
    private String name;
    private int age;
}

Bây giờ, chúng ta sẽ sử dụng Java Reflection để lấy tên của tất cả các field trong person class.

public class Main {

    public static void main(String[] args) {
        Object person = new Person();
        Field[] fields = person.getClass().getDeclaredFields();
        for(Field field : fields) {
            System.out.println("Field name: " + field.getName());
        }
    }
}

Output

Field name: name
Field name: age

Sử dụng Java Reflection khi nào?

Trước khi tìm hiểu sâu hơn về Java reflection, chúng ta cần biết khi nào thì nên và khi nào không nên sử dụng nó để tránh lạm dụng kỹ thuật này vì thông thường nó không được khuyến khích sử dụng.

Trong phạm vi bài viết này, mình sẽ lấy một ví dụ kinh điển về cách sử dụng Java reflection. Giả sử chúng ta quy định cách đặt tên các table trong database theo quy ước như tbl_<table_name>. Trong đó table_name chỉ chứa các ký tự thường và ngăn cách giữa các chữ bởi ký tự _, ví dụ tbl_product_detail.

Nhưng trong Java, để ánh xạ các table này dưới dạng Java class chúng ta sẽ phải tuân thủ một số quy định và giả sử tbl_product_detail sẽ được ánh xạ thành ProductDetail class trong Java. Các field -trường dữ liệu trong Java class cũng có thể khác với các column name trong table.

Như vậy, chúng ta đang gặp một vấn đề đó là sự không đồng nhất giữa tên table trong database và với ánh xạ của nó trong Java class. Để giải quyết vấn đề này, chúng ta có thể sử dụng Java reflection để chuyển đổi field name và class name trong Java thành tên tương ứng trong database khi thực hiện việc save, update một Java object xuống database và ngược lại chuyển dữ liệu từ các table, column name tương ứng sang Java object khi thực hiện việc truy vấn dữ liệu.

Inspect Java Classes

Trong phần này chúng ta sẽ tìm hiểu khái niệm cơ bản nhất trong Java reflection API. Chúng ta sẽ sử dụng reflection để truy xuất các thông tin chi tiết bên trong một class như class name, các fields, methods v.v

Đầu tiên, chúng ta có Eating và Locomotion  interface.

public interface Eating {
    String eats();
}

public interface Locomotion {
    String getLocomotion();
}

Và Animal implementation của Eating interface.

public abstract class Animal implements Eating {

    public static String CATEGORY = "domestic";
    private String name;

    protected abstract String getSound();

    public Animal(String name) {
        this.name = name;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }
}

Cuối cùng chúng ta sẽ tạo ra một Goat class extends từ Animal và implement Locomotion

public class Goat extends Animal implements Locomotion {

    public Goat(String name) {
        super(name);
    }

    @Override
    protected String getSound() {
        return "bleat";
    }

    @Override
    public String getLocomotion() {
        return "walks";
    }

    @Override
    public String eats() {
        return "grass";
    }

}

Class name

Việc đơn giản đầu tiên chúng ta có thể làm với reflection đó là lấy tên của một class.

public class Main {

    public static void main(String[] args) {
        Object goat = new Goat("goat");
        Class<?> clazz = goat.getClass();

        System.out.println(clazz.getSimpleName());
        System.out.println(clazz.getName());
        System.out.println(clazz.getCanonicalName());
    }
}

Output

Goat
com.deft.reflection.Goat
com.deft.reflection.Goat

Trong đó, getSimpleName() chỉ trả về tên class, còn 2 method còn lại getName()getCanonicalName() sẽ trả về tên đầy đủ của class.

Class Modifiers

Chúng ta còn có thể sử dụng reflection để kiểm tra modifiers của một class là public, private thông qua getModifiers() method.

import java.lang.reflect.Modifier;

public class Main {

    public static void main(String[] args) throws ClassNotFoundException {
        Class<?> goatClass = Class.forName("com.deft.reflection.Goat");
        Class<?> animalClass = Class.forName("com.deft.reflection.Animal");

        int goatMods = goatClass.getModifiers();
        int animalMods = animalClass.getModifiers();
        System.out.println(Modifier.isPublic(goatMods));
        System.out.println(Modifier.isPublic(animalMods));
        System.out.println(Modifier.isAbstract(animalMods));
    }
}

Output

true
true
true

Package Information

Bằng cách sử dụng reflection chúng ta có thể lấy thông tin về một package của một class hoặc object bất kỳ thông qua getPackage().

public class Main {

    public static void main(String[] args) throws ClassNotFoundException {
        Goat goat = new Goat("goat");
        Class<?> goatClass = goat.getClass();
        Package pkg = goatClass.getPackage();
        System.out.println( pkg.getName());
    }
}

Output

com.deft.reflection

Super Class

Tương tự, chúng ta có getSuperclass() dùng để lấy thông tin của supper class. Lưu ý chúng ta có thể lấy thông tin supper class của một class bất kỳ chứ không chỉ giới hạn do những class do chúng ta tạo ra.

public class Main {

    public static void main(String[] args) throws ClassNotFoundException {
        Goat goat = new Goat("goat");
        String str = "any string";

        Class<?> goatClass = goat.getClass();
        Class<?> goatSuperClass = goatClass.getSuperclass();

        System.out.println(goatSuperClass.getSimpleName());
        System.out.println(str.getClass().getSuperclass().getSimpleName());
    }
}

Output

Animal
Object

Interfaces

Với Java reflection chúng ta còn có thể lấy danh sách các interface mà class hiện tại đang implement.

public class Main {

    public static void main(String[] args) throws ClassNotFoundException {
        Class<?> goatClass = Class.forName("com.deft.reflection.Goat");
        Class<?> animalClass = Class.forName("com.deft.reflection.Animal");

        Class<?>[] goatInterfaces = goatClass.getInterfaces();
        Class<?>[] animalInterfaces = animalClass.getInterfaces();

        System.out.println("Goat Interfaces: " + goatInterfaces.length);
        System.out.println("Animal Interfaces: " + animalInterfaces.length);
        System.out.println("Goat Interfaces name: " + goatInterfaces[0].getSimpleName());
        System.out.println("Animal Interfaces name: " + animalInterfaces[0].getSimpleName());
    }
}

Output

Goat Interfaces: 1
Animal Interfaces: 1
Goat Interfaces name: Locomotion
Animal Interfaces name: Eating

Chúng ta để ý rằng Goat extends từ Animal và implement Locomotion interface. Animal cũng implement từ Eating vậy trên lý thuyết thì Goat implement cả Locomotion, và Eating interface nhưng kết quả trả về chỉ có Locomotion. Do vậy chúng ta lưu ý rằng getInterfaces() chỉ trả về những interface được khai báo tường minh trong class.

Constructors, Methods, và Fields

Với Java reflection, chúng ta có thể truy cập các constructor của bất kỳ class nào cũng như các method và field trong class.

 

import java.lang.reflect.Constructor;

public class Main {

    public static void main(String[] args) throws ClassNotFoundException {
        Class<?> goatClass = Class.forName("com.deft.reflection.Goat");

        Constructor<?>[] constructors = goatClass.getConstructors();

        System.out.println("Length: " + constructors.length);
        System.out.println("Constructor name: " + constructors[0].getName());
    }
}

Tiếp theo chúng ta sẽ kiểm tra các field trong Animal class

import java.lang.reflect.Field;

public class Main {

    public static void main(String[] args) throws ClassNotFoundException {
        Class<?> animalClass = Class.forName("com.deft.reflection.Animal");
        Field[] fields = animalClass.getDeclaredFields();

        System.out.println("Size: " + fields.length);
        System.out.println(fields[0].getName());
        System.out.println(fields[1].getName());
    }
}

Output

Size: 2
CATEGORY
name

Tương tự chúng ta có thể sử dụng getDeclaredMethods() trong Java reflection để kiểm tra tất cả các method bên trong một class.

import java.lang.reflect.Method;

public class Main {

    public static void main(String[] args) throws ClassNotFoundException {
        Class<?> animalClass = Class.forName("com.deft.reflection.Animal");
        Method[] methods = animalClass.getDeclaredMethods();
        System.out.println("Size: " + methods.length);
        System.out.println(methods[0].getName());
        System.out.println(methods[1].getName());
        System.out.println(methods[2].getName());
    }
}

Tóm lược

Qua đây chúng ta đã biết được một số thao tác cơ bản trong Java reflection. Lưu ý đây là một kỹ thuật tương đối khó và thông thường rất ít được sử dụng. Nếu các bạn là người mới tiếp xúc với ngôn ngữ Java thì cũng đừng quá lo ngại khi cảm thấy bản thân khó tiếp cận với nó.

Nguồn: baeldung

0 0 votes
Article Rating
Subscribe
Notify of
guest
0 Comments
Inline Feedbacks
View all comments
0
Would love your thoughts, please comment.x
()
x