Java Reflection
Java Reflection là gì
Java Reflection là 1 tính năng trong Java, cho phép truy cập các thông tin của đối tượng (tên Class , các field, các method) và chỉnh sửa các field của đối tượng (kể cả các field private) trong quá trình runtime.
Cơ chế hoạt động của Reflection
Đầu tiên cần hiểu 2 định nghĩa về compile time và runtime :
compile time: Là giai đoạn src code được compiler biên dịch thành code mà máy tính có thể thực thi (executable code). Trong Java đó là quá trình biên dịch file.javathành file.class. Trongcompile time, chỉ 1 số chức năng biên dịch được thực hiện và code không được đưa vào bộ nhớ để chạy mà chỉ hoạt động dưới dạng văn bản, chẳng hạn như kiểm tra lỗi.runtime: Là giai đoạn code trên đĩa (disk) được đưa vào bộ nhớ và thực thi.
Ví dụ về 2 định nghĩa trên:
Khi syntax ở dòng 7 bị sai thì compile sẽ bắt lỗi ở đây:
Giờ đến ví dụ chỉnh sửa private property của class bằng Java Reflection:
1 | public class Dog { |
1 | package org.example; |
Output:
1 | Hello and welcome! |
Hình dung hệ thống sẽ phân cấp như sau:
Các class được dùng trong Reflection được nằm trong package java.lang.reflect
Object
Class Object là class gốc trong hệ thống phân lớp các class.
Mọi class đều là con của class Object,hay có thể nói class Object là class cha của toàn bộ các class.
Class
Class là class được cung cấp bởi package java.lang.Class
Một instance của class Class đại diện cho toàn bộ các kiểu dữ liệu trong Java, bao gồm các kiểu dữ liệu cơ bản (boolean, byte, char, short, int, long), void, array, class, interface, enumeration, annotation.
Class không có public constructor. Thay vào đó object của Class được tạo ra tự động bởi JVM trong quả trình tải class.
Khi sử dụng java reflection 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
Có 3 cách để lấy instance của class:
- Sử dụng các biến static class trong class:
1 | Class cls = dog.class; // class là 1 biến static của class Dog |
- Nếu có object của 1 class thì có thể sử dụng method
getClass()
- Nếu có object của 1 class thì có thể sử dụng method
1 | Dog s = new Dog("Hello"); |
- Nếu biết toàn bộ tên của class thì có thể sử dụng
Class.forName()
- Nếu biết toàn bộ tên của class thì có thể sử dụng
1 | Class<?> dogClass = Class.forName("org.example.Dog"); |
Khi đã có được Class Object rồi thì ta sẽ dễ dàng có được thông tin các trường của class với các method được viết sẵn:
1 | Class<?> pugClass = Class.forName("org.example.Pug"); |
Sử dụng phương thức get() để lấy giá trị của field.
Method
Lấy method
Để truy xuất và gọi các method của class thì chúng ta lại có các phương thức có sẵn của Class object
Method getMethod(name, Class object của type param) : lấy public method với tên của nó được truyền vào của class và class cha
Method getDeclaredMethod(name, Class object của type param) : lấy method (cả public và private) với tên của nó được truyền vào của class không bao gồm class cha
Method[] getMethods() : lấy tất cả public method của class gồm cả của class cha
Method[] getDeclaredMethods() : lấy tất cả method của class
Ví dụ:
1 | class Student extends Person{ |
1 | Class stdClass = Student.class; |
Gọi method
Bình thường chúng ta sẽ gọi method như thế này
1 | String s = "hello world"; |
Đối với reflection sử dụng hàm invoke(object, param)
1 | import java.lang.Class; |
Đối với các method khai báo với private thì phải sử dụng thêm setAccessible()
Tính đa hình
Câu hỏi đặt ra là nếu class con override một method của class cha thì khi sử dụng reflection để truy xuất method, method nào sẽ được gọi? Method của class cha hay của class con.
1 | import java.lang.Class; |
Tùy vào object được truyền vào hàm invoke() và vẫn sẽ tuân theo flow của tính đa hình, đó là method của class con sẽ override method của class cha.
Đoạn code trên tương đương với:
1 | Person p = new Student(); |
Truy xuất thông tin các hàm khởi tạo
Bình thường chúng ta có thể khởi tạo một đối tượng bằng cách:
1 | Person p = new Person(); |
Nếu muốn khởi tạo một đối tượng với Reflection thì:
1 | Person p = Person.class.newInstance(); |
Nhưng điểm hạn chế của newInstance() là chúng ta không thể khởi tạo đối tượng có tham số truyền vào.
Đừng lo vì Java Reflection đã tiếp tục cung cấp cho chúng ta Constructor class object với các method có sẵn để lấy được các hàm khởi tạo của class
1 | import java.lang.Class; |
1 | getConstructor(Class...); |
Lấy những thông tin về tính kế thừa của class
Để có được class cha của class hiện tại sử dụng hàm getSupperClass()
1 | import java.lang.Class; |
Để có được list interface của class sử dụng getInterfaces()
1 | import java.lang.Class; |