Phản xạ là gì?
Phản xạ là một API hỗ trợ tạo ra các thể hiện của lớp mong muốn thông qua đối tượng kiểu Class được tải vào vùng heap, và hỗ trợ truy cập vào các trường và phương thức của thể hiện, bất kể bộ sửa đổi truy cập.
Ở đây, lớp được tải có nghĩa là sau khi trình tải lớp JVM hoàn thành việc tải tệp lớp, nó tạo ra một đối tượng kiểu Classchứa thông tin của lớp đó và lưu trữ nó trong vùng heap của bộ nhớ. Lưu ý rằng nó khác với đối tượng được tạo bằng từ khóa new. Nếu bạn chưa hiểu rõ về đối tượng kiểu Class này, hãy tham khảo tài liệu JDK của java.lang.class.
Cách sử dụng
Trước khi sử dụng phản xạ, bạn cần lấy đối tượng kiểu Class được tải vào vùng heap. Có tổng cộng 3 cách.
- Lấy bằng Class.class
- Lấy bằng Instance.getClass()
- Lấy bằng Class.forName("Tên lớp")
Có thể thấy rằng các thể hiện kiểu Class được lấy bằng 3 cách trên đều giống nhau. Bất kể bạn sử dụng phương pháp nào, giá trị băm đều giống nhau, vì vậy bạn có thể sử dụng chúng một cách linh hoạt tùy theo hoàn cảnh.
Bây giờ, thông qua kiểu Class đã lấy được, chúng ta có thể tạo ra thể hiện của lớp đó và truy cập vào các trường và phương thức của thể hiện, bất kể bộ sửa đổi truy cập. Đầu tiên, hãy tạo thể hiện của lớp đó.
Chúng ta có thể lấy hàm tạo thông qua getConstructor() và tạo thể hiện Member một cách động thông qua newInstance().
Cuối cùng, hãy truy cập và sử dụng các trường và phương thức của thể hiện, bất kể bộ sửa đổi truy cập.
Chúng ta có thể lấy tất cả các biến thành viên của lớp thông qua getDeclaredFileds(), trả về giá trị trường thông qua get() và sửa đổi giá trị trường thông qua set(). Lưu ý rằng khi truy cập vào các trường có bộ sửa đổi truy cập là private, bạn cần đặt tham số của setAccessible() thành true.
Đối với phương thức, chúng ta cũng có thể lấy phương thức thông qua getDeclaredMethod(). Lúc này, bạn cần truyền tên phương thức và kiểu dữ liệu của tham số vào làm tham số. Tương tự, khi truy cập vào các phương thức có bộ sửa đổi truy cập là private, bạn cần đặt tham số của setAccessible() thành true. Cuối cùng, chúng ta có thể gọi phương thức đã lấy được thông qua API phản xạ bằng phương thức invoke().
Ưu & nhược điểm
- Ưu điểm
- Có tính linh hoạt, có thể tạo thể hiện lớp và truy cập vào các trường và phương thức tại thời điểm chạy, bất kể bộ sửa đổi truy cập, để thực hiện các tác vụ cần thiết.
- Nhược điểm
- Làm suy yếu tính đóng gói.
- Vì tạo thể hiện tại thời điểm chạy nên không thể kiểm tra kiểu tại thời điểm biên dịch.
- Vì tạo thể hiện tại thời điểm chạy nên khó theo dõi luồng hoạt động cụ thể.
- Hiệu suất chậm hơn so với việc truy cập trực tiếp vào trường và phương thức (không phải trong mọi trường hợp).
Lý do sử dụng
Thông qua API phản xạ, chúng ta có thể truy cập thông tin lớp và điều khiển lớp theo ý muốn trong thời gian chạy. Thậm chí, chúng ta có thể điều khiển cả các trường và phương thức được khai báo là private. Điều này có vẻ như là một kỹ thuật không nên sử dụng vì nó làm suy yếu tính đóng gói, một khái niệm quan trọng trong thiết kế hướng đối tượng.
Đối với các ứng dụng quy mô nhỏ ở cấp độ console, nhà phát triển có thể dễ dàng xác định tất cả các đối tượng và mối quan hệ phụ thuộc được sử dụng trong chương trình tại thời điểm biên dịch. Tuy nhiên, đối với các dự án quy mô lớn như framework, việc xác định hàng loạt đối tượng và mối quan hệ phụ thuộc là rất khó khăn. Lúc này, phản xạ có thể giúp tạo ra các lớp một cách động và thiết lập mối quan hệ phụ thuộc.
Ví dụ, trong Bean Factory của Spring, chỉ cần thêm các chú thích @Controller, @Service, @Repository, Bean Factory sẽ tự động tạo ra và quản lý các lớp có chú thích đó. Nhà phát triển chưa bao giờ cung cấp cho Bean Factory thông tin về các lớp này, nhưng điều này có thể thực hiện được nhờ phản xạ. Tại thời điểm chạy, nó sẽ tìm kiếm và phát hiện các lớp có chú thích đó, sau đó sử dụng phản xạ để tạo thể hiện của các lớp này, tiêm các trường cần thiết và lưu trữ chúng trong Bean Factory.
Tất nhiên, như đã đề cập ở trên, nó làm suy yếu tính đóng gói, vì vậy tốt nhất là chỉ sử dụng nó khi thực sự cần thiết.
Bình luận0