■概要
Javaのリフレクション(反射)メカニズムとは、実行時にクラス情報を取得したり、オブジェクトを作成したり、メソッドを呼び出したり、属性にアクセスしたりする機能を持つメカニズムのことです。リフレクションを使うことで、実行時に動的にJavaオブジェクトを操作できるようになり、コンパイル時に具体的なクラスやメソッドを決定する必要がなくなります。これにより、Javaプログラムはより柔軟性と拡張性が向上します。
リフレクションメカニズムは主に以下の点に関連しています。
-
Classオブジェクトの取得:リフレクションの最初のステップは、対象クラスのClassオブジェクトを取得することです。Classオブジェクトを取得する方法は3つあります。
- クラスの完全修飾名(パッケージ名+クラス名)を使ってClass.forName()メソッドを呼び出す。
- オブジェクトのgetClass()メソッドを呼び出す。
- クラスリテラルを使用する(例:String.class)。
-
クラス情報の取得:Classオブジェクトを使って、クラス名、パッケージ名、親クラス、インターフェース、コンストラクタ、メソッド、属性などのクラス情報を取得できます。これらの情報は、Classオブジェクトの関連メソッドを使って取得できます。例えば:
- getSimpleName():クラス名を取得。
- getPackage():パッケージ情報を取得。
- getSuperclass():親クラスを取得。
- getInterfaces():実装されているインターフェースを取得。
- getConstructors():コンストラクタを取得。
- getMethods():メソッドを取得。
- getFields():属性を取得。
-
オブジェクトの作成:Classオブジェクトを使って、対象クラスのインスタンスを作成できます。オブジェクトを作成する方法は2つあります。
- ClassオブジェクトのnewInstance()メソッドを呼び出す。これには、対象クラスに引数なしのコンストラクタが必要です。
- ConstructorオブジェクトのnewInstance()メソッドを使って、コンストラクタに必要な引数を渡す。
-
属性へのアクセス:リフレクションを使って、オブジェクトの属性値を取得・変更できます。これはFieldオブジェクトを使って実現できます。例えば:
- get():属性値を取得。
- set():属性値を設定。
- setAccessible():属性のアクセス権限を設定し、プライベート属性へのアクセスを許可する。
-
メソッドの呼び出し:リフレクションを使って、オブジェクトのメソッドを動的に呼び出すことができます。これはMethodオブジェクトを使って実現できます。例えば:
- invoke():メソッドを呼び出し、オブジェクトインスタンスとメソッド引数を渡す。
- setAccessible():メソッドのアクセス権限を設定し、プライベートメソッドへのアクセスを許可する。
Javaのリフレクションメカニズムは、プログラムの柔軟性と拡張性を向上させる利点がありますが、同時にパフォーマンスのオーバーヘッドやセキュリティリスクも伴います。そのため、実際の開発では、具体的な要件に応じてリフレクションの使用を検討する必要があります。
Java的反射机制是指在运行时能够获取类的信息、创建对象、调用方法、访问属性等功能的一种机制。通过反射,我们可以在运行时动态地操作Java对象,而不需要在编译时就确定具体的类和方法。这使得Java程序具有更高的灵活性和扩展性。 反射机制主要涉及到以下几个方面: 获取Class对象:反射的第一步是获取目标类的Class对象。有三种方法可以获取Class对象: 使用类的全限定名(包名+类名)调用Class.forName()方法; 调用某个对象的getClass()方法; 使用类字面量(如:String.class)。 获取类的信息:通过Class对象,我们可以获取类的各种信息,如类名、包名、父类、接口、构造器、方法、属性等。这些信息可以通过Class对象的相关方法获取,如: getSimpleName():获取类名; getPackage():获取包信息; getSuperclass():获取父类; getInterfaces():获取实现的接口; getConstructors():获取构造器; getMethods():获取方法; getFields():获取属性。 创建对象:通过Class对象,我们可以创建目标类的实例。有两种方法可以创建对象: 调用Class对象的newInstance()方法,这要求目标类有一个无参构造器; 通过Constructor对象的newInstance()方法,可以传入构造器需要的参数。 访问属性:通过反射,我们可以获取和修改对象的属性值。这可以通过Field对象实现,如: get():获取属性值; set():设置属性值; setAccessible():设置属性的访问权限,允许访问私有属性。 调用方法:通过反射,我们可以动态地调用对象的方法。这可以通过Method对象实现,如: invoke():调用方法,传入对象实例和方法参数; setAccessible():设置方法的访问权限,允许访问私有方法。 Java反射机制的优点是提高了程序的灵活性和扩展性,但同时也带来了一定的性能开销和安全风险。因此,在实际开发中,我们应该根据具体需求权衡是否使用反射。
■ベースサンプル
public class Person { private String name; private int age; public Person() { } public Person(String name, int age) { this.name = name; this.age = age; } public String getName() { return name; } public void setName(String name) { this.name = name; } public int getAge() { return age; } public void setAge(int age) { this.age = age; } public void introduce() { System.out.println("Hello, my name is " + name + " and I am " + age + " years old."); } }
import java.lang.reflect.Constructor; import java.lang.reflect.Field; import java.lang.reflect.Method; public class ReflectionExample { public static void main(String[] args) { try { // 获取Class对象 Class<?> personClass = Class.forName("Person"); // 获取类信息 System.out.println("Class name: " + personClass.getSimpleName()); System.out.println("Package name: " + personClass.getPackage().getName()); // 创建对象 Constructor<?> constructor = personClass.getConstructor(String.class, int.class); Object person = constructor.newInstance("Alice", 30); // 访问属性 Field nameField = personClass.getDeclaredField("name"); Field ageField = personClass.getDeclaredField("age"); nameField.setAccessible(true); ageField.setAccessible(true); System.out.println("Name: " + nameField.get(person)); System.out.println("Age: " + ageField.get(person)); // 调用方法 Method setNameMethod = personClass.getMethod("setName", String.class); Method setAgeMethod = personClass.getMethod("setAge", int.class); Method introduceMethod = personClass.getMethod("introduce"); setNameMethod.invoke(person, "Bob"); setAgeMethod.invoke(person, 25); introduceMethod.invoke(person); } catch (Exception e) { e.printStackTrace(); } } }
Class name: Person Package name: (default package) Name: Alice Age: 30 Hello, my name is Bob and I am 25 years old.
この例では、まずClass.forName()
メソッドを使ってPerson
クラスのClass
オブジェクトを取得します。次に、Class
オブジェクトを使ってクラス名とパッケージ名を取得します。その後、リフレクションを使ってPerson
クラスのインスタンスを作成し、その属性にアクセスします。最後に、リフレクションを使ってPerson
クラスのメソッドを呼び出します。
■サンプル1:インタフェースを利用して、対象の間に、同じ属性をコピーする
package org.example.treflect; import java.lang.reflect.Constructor; import java.lang.reflect.Field; public interface BaseData { /** * 将当前对象(实现BaseData接口的对象)的属性值转换并设置到目标类(clazz)的新实例中 * * @return */ default <T> T asViewObject(Class<T> clazz) { try { //获取目标Class中的所有属性对象(不包含父类) Field[] targetFields = clazz.getDeclaredFields(); //获取目标Class中的所有构造函数(不包含父类) Constructor<T> constructor = clazz.getDeclaredConstructor(); //新实例化一个目标Class对象(内部全部是空值) T newTarget = constructor.newInstance(); //遍历目标Class中的所有属性对象 for (Field targetField:targetFields){ //参数1:目标Class对象的属性 //参数2:新实例化对象 convert(targetField,newTarget); } //返回新目标对象 return newTarget; }catch (ReflectiveOperationException exception){ throw new RuntimeException(exception.getMessage()); } } /** * 利用反射,将源对象(this)的属性值复制到目标对象(newTarget)的对应属性中 * * @param targetField 目标对象的属性 * @param newTarget 目标对象 */ private void convert(Field targetField, Object newTarget) { try{ //根据目标对象的属性名称,获取本接口的实现Class中的所对应的属性(即SourceField) Field sourceField = this.getClass().getDeclaredField(targetField.getName()); //设定是否可以访问(目标) targetField.setAccessible(true); //设定是否可以访问(源) sourceField.setAccessible(true); //【 源.属性.值 -> 目标.属性】 //反射1:从this中获取source field属性的值【get()】 //反射2:将值反射到newTarget中的targetField属性中【set()】 targetField.set(newTarget,sourceField.get(this)); }catch (NoSuchFieldException | IllegalAccessException exception){ System.err.println(exception.getMessage()); } } }
■サンプル2:
package org.example.treflect; import javax.xml.crypto.Data; import java.lang.reflect.Field; import java.util.Arrays; import java.util.Date; /** * @version: java version 1.8 * @Author: Mr Orange * @description: * @date: 2024-02-01 6:16 AM */ public class ReflectMain { public ReflectMain(){ SampleSource source =new SampleSource(); source.setUserName("litao"); source.setPassWord("123456"); source.setAddress("shenyang"); System.out.println(source); //方法一 SampleTarget target1 = source.asViewObject(SampleTarget.class); target1.setRole("project manager"); target1.setToken("abcdefghijklmnopqrstuvwxyz"); target1.setExpire(new Date().getTime()); System.out.println("target1:"+target1); //方法二 SampleTarget target2 = source.asViewObject(SampleTarget.class,t->{ t.setRole("project manager"); t.setToken("abcdefghijklmnopqrstuvwxyz"); t.setExpire(new Date().getTime()); }); System.out.println("target2:"+target2); } public static void main(String[] args){ new ReflectMain(); } }
package org.example.treflect; import java.lang.reflect.Constructor; import java.lang.reflect.Field; import java.util.function.Consumer; public interface BaseData { default <T> T asViewObject(Class<T> clazz, Consumer<T> consumer){ //获取实例 T t = this.asViewObject(clazz); consumer.accept(t); return t; } /** * 将当前对象(实现BaseData接口的对象)的属性值转换并设置到目标类(clazz)的新实例中 * * @return */ default <T> T asViewObject(Class<T> clazz) { try { //获取目标Class中的所有属性对象(不包含父类) Field[] targetFields = clazz.getDeclaredFields(); //获取目标Class中的所有构造函数(不包含父类) Constructor<T> constructor = clazz.getDeclaredConstructor(); //新实例化一个目标Class对象(内部全部是空值) T newTarget = constructor.newInstance(); //遍历目标Class中的所有属性对象 for (Field targetField:targetFields){ //参数1:目标Class对象的属性 //参数2:新实例化对象 convert(targetField,newTarget); } //返回新目标对象 return newTarget; }catch (ReflectiveOperationException exception){ throw new RuntimeException(exception.getMessage()); } } /** * 利用反射,将源对象(this)的属性值复制到目标对象(newTarget)的对应属性中 * * @param targetField 目标对象的属性 * @param newTarget 目标对象 */ private void convert(Field targetField, Object newTarget) { try{ //根据目标对象的属性名称,获取本接口的实现Class中的所对应的属性(即SourceField) Field sourceField = this.getClass().getDeclaredField(targetField.getName()); //设定是否可以访问(目标) targetField.setAccessible(true); //设定是否可以访问(源) sourceField.setAccessible(true); //【 源.属性.值 -> 目标.属性】 //反射1:从this中获取source field属性的值【get()】 //反射2:将值反射到newTarget中的targetField属性中【set()】 targetField.set(newTarget,sourceField.get(this)); }catch (NoSuchFieldException | IllegalAccessException exception){ System.err.println(exception.getMessage()); } } }