Java reflection là 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.
Trong bài viết này chúng ta sẽ cùng nhau tìm hiểu cách sử dụng Java reflection để thao tác với constructor, trường dữ liệu – field, và method trong Java.
Chuẩn bị
Trước khi tìm hiểu cách sử dụng Java reflection để thao tác với constructor, field và constructor chúng ta cần chuẩn bị một số thứ dùng cho các ví dụ ở các phần sau.
public interface Eating { String eats(); }
Animal abstract class implement từ 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 là Bird class extends từ Animal class
public class Bird extends Animal { private boolean walks; public Bird() { super("bird"); } public Bird(String name, boolean walks) { super(name); setWalks(walks); } public boolean isWalks() { return walks; } public void setWalks(boolean walks) { this.walks = walks; } public Bird(String name) { super(name); } public boolean walks() { return walks; } @Override public String eats() { return null; } @Override protected String getSound() { return null; } }
Thao tác với constructor
Với Java reflection có ta có thể kiểm tra các constructor của một class bất kỳ tại thời điểm runtime. Hoặc thậm chí chúng ta có thể khởi tạo một object của class này với java.lang.reflect.Constructor class.
Việc đơn giản nhất chúng ta có thể thao tác đầu tiên đó là kiểm tra tất cả các constructor có trong một class – trong ví dụ này là Bird class.
import java.lang.reflect.Constructor; public class Main { public static void main(String[] args) throws ClassNotFoundException { Class<?> birdClass = Class.forName("com.deft.reflection.Bird"); Constructor<?>[] constructors = birdClass.getConstructors(); System.out.println("Length: " + constructors.length); } }
Output: Length: 3
Bây giờ chúng ta có thể lấy ra bất kỳ một constructor dựa vào tham số đầu vào
import java.lang.reflect.Constructor; public class Main { public static void main(String[] args) throws NoSuchMethodException, ClassNotFoundException { Class<?> birdClass = Class.forName("com.deft.reflection.Bird"); Constructor<?> cons1 = birdClass.getConstructor(); Constructor<?> cons2 = birdClass.getConstructor(String.class); Constructor<?> cons3 = birdClass.getConstructor(String.class, boolean.class); } }
Chúng ta cần lưu ý, nếu getConstructor(parameters) không tìm thấy được một constructor tương ứng nó sẽ quăng NoSuchMethodException.
Sau khi đã lấy được các constructor instance về chúng ta có thể khởi tạo các Bird object từ các constructors instance như sau
import java.lang.reflect.Constructor; public class Main { public static void main(String[] args) throws Exception { Class<?> birdClass = Class.forName("com.deft.reflection.Bird"); Constructor<?> cons1 = birdClass.getConstructor(); Constructor<?> cons2 = birdClass.getConstructor(String.class); Constructor<?> cons3 = birdClass.getConstructor(String.class, boolean.class); Bird bird1 = (Bird) cons1.newInstance(); Bird bird2 = (Bird) cons2.newInstance("Weaver bird"); Bird bird3 = (Bird) cons3.newInstance("dove", true); System.out.println("Bird 1: " + bird1.getName()); System.out.println("Bird 2: " + bird2.getName()); System.out.println("Bird 3: " + bird3.getName()); } }
Output
Bird 1: bird Bird 2: Weaver bird Bird 3: dove
Thao tác với field
Có hai phương thức chính được sử dụng để kiểm tra các field trong một class tại thời điểm runtime: getFields() và getField(fieldName).
Method getFields() trả về tất cả các public field của một class bao gồm cả các public field của class cha.
import java.lang.reflect.Field; public class Main { public static void main(String[] args) throws Exception { Class<?> birdClass = Class.forName("com.deft.reflection.Bird"); Field[] fields = birdClass.getFields(); System.out.println("Length: " + fields.length); System.out.println("Field name: " + fields[0].getName()); } }
Output
Length: 1 Field name: CATEGORY
Chúng ta có thể thấy rằng field name không được trả về vì nó là private field được định nghĩa trong Animal.
Method getField() dùng để lấy một Field instance trực tiếp dựa vào tên của biến được truyền vào
import java.lang.reflect.Field; public class Main { public static void main(String[] args) throws Exception { Class<?> birdClass = Class.forName("com.deft.reflection.Bird"); Field field = birdClass.getField("CATEGORY"); System.out.println("Field name: " + field.getName()); } }
Output: Field name: CATEGORY
Chúng ta không thể truy cập được các private field được khai báo trong class cha, nhưng đối với các private field được khai báo trong class con thì chúng ta vẫn có thể truy cập được thông qua getDeclaredFields() method
import java.lang.reflect.Field; public class Main { public static void main(String[] args) throws Exception { Class<?> birdClass = Class.forName("com.deft.reflection.Bird"); Field[] fields = birdClass.getDeclaredFields(); System.out.println("Length: " + fields.length); System.out.println(fields[0].getName()); } }
Output:
Length: 1 walks
Tương tự chúng ta có thể sử dụng getDeclaredField(name) để lấy một field bất kỳ trong một class bao gồm private.
import java.lang.reflect.Field; public class Main { public static void main(String[] args) throws Exception { Class<?> birdClass = Class.forName("com.deft.reflection.Bird"); Field field = birdClass.getDeclaredField("walks"); System.out.println(field.getName()); } }
Output: walks
Ngoài ra để biết thông tin như kiểu dữ liệu của field thì chúng ta có thể sử dụng Field#getType().
public class Main { public static void main(String[] args) throws Exception { Class<?> birdClass = Class.forName("com.deft.reflection.Bird"); Field field = birdClass.getDeclaredField("walks"); System.out.println("Type: " + field.getType()); } }
Output: Type: boolean
Tiếp theo là phần hay nhất chúng ta có thể sử dụng reflection để truy cập giá trị của các field kể cả private hoặc thậm chí là chỉnh sửa giá trị của chúng.
public class Main { public static void main(String[] args) throws Exception { Class<?> birdClass = Class.forName("com.deft.reflection.Bird"); Bird bird = (Bird) birdClass.getConstructor().newInstance(); Field field = birdClass.getDeclaredField("walks"); field.setAccessible(true); System.out.println("Before"); System.out.println("Reflection get: " + field.getBoolean(bird)); System.out.println("Instance get: " + bird.walks()); field.set(bird, true); System.out.println("After"); System.out.println("Reflection get: " + field.getBoolean(bird)); System.out.println("Instance get: " + bird.walks()); } }
Trong đó, điểm quan trọng mà chúng ta cần lưu ý đó là việc sử dụng field.setAccessible(true) sẽ giúp chúng ta có thể truy cập chỉnh sửa field này một cách thoải mái.
Một lưu ý nữa với public static field thì chúng ta không cần phải dùng đến instance để truy cập vào chúng chúng ta chỉ có thể chuyển null vào hàm field.get() để lấy giá trị mặc định.
public class Main { public static void main(String[] args) throws Exception { Class<?> birdClass = Class.forName("com.deft.reflection.Bird"); Field field = birdClass.getField("CATEGORY"); field.setAccessible(true); System.out.println(field.get(null)); } }
Output: domestic
Thao tác Method
Với Java reflection chúng ta không chỉ lấy được tên các method mà còn có gọi thực thi những method này tại thời điểm runtime.
Cũng giống như các field, có 2 method chính mà chúng ta sử dụng để truy xuất các method của class là getMethods() trả về một mảng tất cả các public method của class hiện tại và class cha của nó.
public class Main { public static void main(String[] args) throws Exception { Class<?> birdClass = Class.forName("com.deft.reflection.Bird"); Method[] methods = birdClass.getMethods(); System.out.println("length: " + methods.length); System.out.println("Method list"); for(Method method : methods) { System.out.println(method.getName()); } } }
Output
length: 15 Method list eats setWalks walks isWalks getName setName wait wait wait equals toString hashCode getClass notify notifyAll
Nếu chỉ quan tâm đến các public method thì chúng ta phải sử dụng phương thức getDeclaredMethods()
public class Main { public static void main(String[] args) throws Exception { Class<?> birdClass = Class.forName("com.deft.reflection.Bird"); Method[] methods = birdClass.getDeclaredMethods(); System.out.println("length: " + methods.length); System.out.println("Method list"); for(Method method : methods) { System.out.println(method.getName()); } } }
Output
length: 5 Method list isWalks walks getSound setWalks eats
Tiếp theo chúng ta sẽ dùng Java reflection để gọi một method trong Bird method ngay tại thời điểm runtime
public class Main { public static void main(String[] args) throws Exception { Class<?> birdClass = Class.forName("com.deft.reflection.Bird"); Bird bird = (Bird) birdClass.getConstructor().newInstance(); Method setWalksMethod = birdClass.getDeclaredMethod("setWalks", boolean.class); Method walksMethod = birdClass.getDeclaredMethod("walks"); boolean walks = (boolean) walksMethod.invoke(bird); System.out.println("walks-reflection: " + walks); System.out.println("walks-instance+ " + bird.walks()); setWalksMethod.invoke(bird, true); System.out.println("----------------------------------------------"); boolean walks2 = (boolean) walksMethod.invoke(bird); System.out.println("walks-reflection: " + walks2); System.out.println("walks-instance+ " + bird.walks()); } }
Output
walks-reflection: false walks-instance+ false ---------------------------------------------- walks-reflection: true walks-instance+ true
Tóm lược
Qua bài viết này chúng ta thấy được sức mạnh của Java reflection, có thể dùng để truy cập và thậm chí là thực hiện lời gọi hàm, khởi tạo đối tượng tại thời điểm runtime. Chúng ta có thể thấy rằng việc sử dụng Java reflection có thể phá vỡ mọi quy tắc trong lập trình hướng đối tượng nên nó thường không được khuyến khích sử dụng.
Nguồn: Baeldung