リフレクション
リフレクション(Reflection)
Section titled “リフレクション(Reflection)”リフレクションは、実行時にクラスの情報を取得し、動的に操作する機能です。この章では、リフレクションの使い方について詳しく解説します。
リフレクションとは
Section titled “リフレクションとは”リフレクションを使用すると、実行時にクラスのメタデータ(フィールド、メソッド、コンストラクタなど)にアクセスし、動的に操作できます。
用途:
- フレームワークの実装(Spring、JPAなど)
- テストフレームワーク(JUnit、Mockitoなど)
- シリアライゼーション(Jackson、Gsonなど)
- 動的プロキシの作成
Classオブジェクトの取得
Section titled “Classオブジェクトの取得”// 方法1: クラスリテラルClass<String> stringClass = String.class;
// 方法2: オブジェクトから取得String str = "Hello";Class<?> strClass = str.getClass();
// 方法3: クラス名から取得Class<?> clazz = Class.forName("java.lang.String");
// 方法4: プリミティブ型Class<Integer> intClass = int.class;Class<Integer> integerClass = Integer.class;クラス情報の取得
Section titled “クラス情報の取得”Class<?> clazz = String.class;
// クラス名String simpleName = clazz.getSimpleName(); // "String"String canonicalName = clazz.getCanonicalName(); // "java.lang.String"String name = clazz.getName(); // "java.lang.String"
// 修飾子int modifiers = clazz.getModifiers();boolean isPublic = Modifier.isPublic(modifiers);boolean isAbstract = Modifier.isAbstract(modifiers);boolean isFinal = Modifier.isFinal(modifiers);
// スーパークラスClass<?> superClass = clazz.getSuperclass();
// 実装しているインターフェースClass<?>[] interfaces = clazz.getInterfaces();
// パッケージPackage pkg = clazz.getPackage();フィールドの操作
Section titled “フィールドの操作”class Person { private String name; public int age; protected String email;}
Class<?> clazz = Person.class;
// すべてのフィールドを取得Field[] allFields = clazz.getDeclaredFields();
// 公開フィールドを取得Field[] publicFields = clazz.getFields();
// 特定のフィールドを取得try { Field nameField = clazz.getDeclaredField("name");
// フィールドの情報 String fieldName = nameField.getName(); Class<?> fieldType = nameField.getType(); int fieldModifiers = nameField.getModifiers();
// フィールドの値の取得・設定 Person person = new Person(); nameField.setAccessible(true); // privateフィールドにアクセス nameField.set(person, "Alice"); String name = (String) nameField.get(person);
} catch (NoSuchFieldException | IllegalAccessException e) { e.printStackTrace();}メソッドの操作
Section titled “メソッドの操作”class Calculator { public int add(int a, int b) { return a + b; }
private int multiply(int a, int b) { return a * b; }}
Class<?> clazz = Calculator.class;
// すべてのメソッドを取得Method[] allMethods = clazz.getDeclaredMethods();
// 公開メソッドを取得Method[] publicMethods = clazz.getMethods();
// 特定のメソッドを取得try { Method addMethod = clazz.getMethod("add", int.class, int.class);
// メソッドの情報 String methodName = addMethod.getName(); Class<?> returnType = addMethod.getReturnType(); Class<?>[] parameterTypes = addMethod.getParameterTypes();
// メソッドの呼び出し Calculator calc = new Calculator(); Object result = addMethod.invoke(calc, 3, 5); System.out.println(result); // 8
// privateメソッドの呼び出し Method multiplyMethod = clazz.getDeclaredMethod("multiply", int.class, int.class); multiplyMethod.setAccessible(true); Object result2 = multiplyMethod.invoke(calc, 3, 5); System.out.println(result2); // 15
} catch (NoSuchMethodException | IllegalAccessException | InvocationTargetException e) { e.printStackTrace();}コンストラクタの操作
Section titled “コンストラクタの操作”class Person { private String name; private int age;
public Person() {}
public Person(String name) { this.name = name; }
public Person(String name, int age) { this.name = name; this.age = age; }}
Class<?> clazz = Person.class;
// すべてのコンストラクタを取得Constructor<?>[] constructors = clazz.getDeclaredConstructors();
// 公開コンストラクタを取得Constructor<?>[] publicConstructors = clazz.getConstructors();
// 特定のコンストラクタを取得try { // 引数なしコンストラクタ Constructor<?> noArgConstructor = clazz.getConstructor(); Person person1 = (Person) noArgConstructor.newInstance();
// 引数1つのコンストラクタ Constructor<?> oneArgConstructor = clazz.getConstructor(String.class); Person person2 = (Person) oneArgConstructor.newInstance("Alice");
// 引数2つのコンストラクタ Constructor<?> twoArgConstructor = clazz.getConstructor(String.class, int.class); Person person3 = (Person) twoArgConstructor.newInstance("Bob", 30);
} catch (NoSuchMethodException | InstantiationException | IllegalAccessException | InvocationTargetException e) { e.printStackTrace();}アノテーションの操作
Section titled “アノテーションの操作”@Retention(RetentionPolicy.RUNTIME)@Target(ElementType.METHOD)@interface MyAnnotation { String value() default ""; int count() default 0;}
class MyClass { @MyAnnotation(value = "test", count = 5) public void myMethod() { }}
Class<?> clazz = MyClass.class;
// クラスレベルのアノテーションAnnotation[] classAnnotations = clazz.getAnnotations();
// 特定のアノテーションを取得MyAnnotation annotation = clazz.getAnnotation(MyAnnotation.class);
// メソッドのアノテーションtry { Method method = clazz.getMethod("myMethod"); MyAnnotation methodAnnotation = method.getAnnotation(MyAnnotation.class);
if (methodAnnotation != null) { String value = methodAnnotation.value(); int count = methodAnnotation.count(); System.out.println("Value: " + value + ", Count: " + count); }
} catch (NoSuchMethodException e) { e.printStackTrace();}動的プロキシ
Section titled “動的プロキシ”import java.lang.reflect.InvocationHandler;import java.lang.reflect.Proxy;
interface UserService { void createUser(String name); void deleteUser(String name);}
class UserServiceImpl implements UserService { @Override public void createUser(String name) { System.out.println("Creating user: " + name); }
@Override public void deleteUser(String name) { System.out.println("Deleting user: " + name); }}
// InvocationHandlerの実装class LoggingHandler implements InvocationHandler { private Object target;
public LoggingHandler(Object target) { this.target = target; }
@Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { System.out.println("Calling method: " + method.getName()); long start = System.currentTimeMillis();
Object result = method.invoke(target, args);
long duration = System.currentTimeMillis() - start; System.out.println("Method " + method.getName() + " took " + duration + "ms");
return result; }}
// プロキシの作成UserService realService = new UserServiceImpl();InvocationHandler handler = new LoggingHandler(realService);
UserService proxy = (UserService) Proxy.newProxyInstance( UserService.class.getClassLoader(), new Class[]{UserService.class}, handler);
// プロキシ経由でメソッドを呼び出しproxy.createUser("Alice"); // ログが出力されるオブジェクトのシリアライゼーション
Section titled “オブジェクトのシリアライゼーション”public class ObjectSerializer {
public Map<String, Object> toMap(Object obj) throws IllegalAccessException { Map<String, Object> map = new HashMap<>(); Class<?> clazz = obj.getClass();
for (Field field : clazz.getDeclaredFields()) { field.setAccessible(true); Object value = field.get(field); map.put(field.getName(), value); }
return map; }
public <T> T fromMap(Map<String, Object> map, Class<T> clazz) throws NoSuchMethodException, IllegalAccessException, InstantiationException, InvocationTargetException { T instance = clazz.getDeclaredConstructor().newInstance();
for (Field field : clazz.getDeclaredFields()) { field.setAccessible(true); Object value = map.get(field.getName()); if (value != null) { field.set(instance, value); } }
return instance; }}パフォーマンスの考慮
Section titled “パフォーマンスの考慮”リフレクションは便利ですが、パフォーマンスに影響を与える可能性があります。
// 遅い: 毎回リフレクションを使用for (int i = 0; i < 1000000; i++) { Method method = clazz.getMethod("methodName"); method.invoke(obj, args);}
// 速い: メソッドを一度取得して再利用Method method = clazz.getMethod("methodName");method.setAccessible(true); // アクセスチェックをスキップfor (int i = 0; i < 1000000; i++) { method.invoke(obj, args);}リフレクションのポイント:
- Classオブジェクト: クラス情報へのアクセス
- フィールド操作: フィールドの取得・設定
- メソッド操作: メソッドの取得・呼び出し
- コンストラクタ操作: インスタンスの動的作成
- アノテーション: 実行時のアノテーション情報取得
- 動的プロキシ: プロキシパターンの実装
- パフォーマンス: 適切なキャッシュと最適化
リフレクションを適切に使用することで、柔軟で拡張性の高いコードを書けます。