Slide 1

Slide 1 text

Javaのルールをねじ曲げろ! 禁断の操作とその代償から学ぶメタプログラミング入門 nrs

Slide 2

Slide 2 text

2 Profile nrs(成瀬 允宣) @nrslib 株式会社コドモン 執行役員CTO 趣味: カンファレンス講演 学生支援 小学校支援 写真

Slide 3

Slide 3 text

3 今日のお話

Slide 4

Slide 4 text

4 今日のお話

Slide 5

Slide 5 text

5 今日のお話

Slide 6

Slide 6 text

6 今日のお話

Slide 7

Slide 7 text

7 今日のお話

Slide 8

Slide 8 text

はじめに 禁断の操作!? 学び まとめ 禁断の操作とその代償から学ぶメタプログラミング入門

Slide 9

Slide 9 text

はじめに 禁断の操作!? 得られるもの まとめ 禁断の操作とその代償から学ぶメタプログラミング入門

Slide 10

Slide 10 text

こんなことを思ったことはありませんか!?

Slide 11

Slide 11 text

魔法使いになりたい

Slide 12

Slide 12 text

12 ● 普通はできないことをやってのける! メタプログラミングは魔法のよう

Slide 13

Slide 13 text

13 ● private なフィールドにアクセスしたり ● private なメソッドにアクセスしたり ● 定義されてないクラスをインスタンス化したり メタプログラミングは魔法のよう

Slide 14

Slide 14 text

とっても便利ですね!!!

Slide 15

Slide 15 text

15 ● メタの意味 メタプログラミングとは 「高位の」「上位の」 といった意味

Slide 16

Slide 16 text

16 ● コード自体をデータとして扱う技術 メタプログラミングとは 通常はデータを処理するコード メタプロはコード自体を扱う

Slide 17

Slide 17 text

はじめに 禁断の操作!? 得られるもの まとめ 禁断の操作とその代償から学ぶメタプログラミング入門

Slide 18

Slide 18 text

18 ● クラスの定義を扱う メタプログラミングの基本 var myClass = new MyClass(); var targetClazz = myClass.getClass();

Slide 19

Slide 19 text

public class MyClass { private int privateField = 1; private void privateMethod() { System.out.println("This is a private method."); } }

Slide 20

Slide 20 text

public class MyClass { private int privateField = 1; private void privateMethod() { System.out.println("This is a private method."); } } 恥ずかしがり屋さん

Slide 21

Slide 21 text

try { var myClass = new MyClass(); var targetClazz = myClass.getClass(); var privateField = targetClazz.getDeclaredField("privateField"); privateField.setAccessible(true); var privateFieldValue = privateField.get(myClass); System.out.printf("My private field is: %s\n", privateFieldValue); var privateMethod = targetClazz.getDeclaredMethod("privateMethod"); privateMethod.setAccessible(true); privateMethod.invoke(myClass); } catch (NoSuchFieldException | IllegalAccessException | NoSuchMethodException | InvocationTargetException e) { System.err.println("基本リフレクションエラー: " + e.getMessage()); }

Slide 22

Slide 22 text

try { var myClass = new MyClass(); var targetClazz = myClass.getClass(); var privateField = targetClazz.getDeclaredField("privateField"); privateField.setAccessible(true); var privateFieldValue = privateField.get(myClass); System.out.printf("My private field is: %s\n", privateFieldValue); var privateMethod = targetClazz.getDeclaredMethod("privateMethod"); privateMethod.setAccessible(true); privateMethod.invoke(myClass); } catch (NoSuchFieldException | IllegalAccessException | NoSuchMethodException | InvocationTargetException e) { System.err.println("基本リフレクションエラー: " + e.getMessage()); } クラス定義を取得

Slide 23

Slide 23 text

try { var myClass = new MyClass(); var targetClazz = myClass.getClass(); var privateField = targetClazz.getDeclaredField("privateField"); privateField.setAccessible(true); var privateFieldValue = privateField.get(myClass); System.out.printf("My private field is: %s\n", privateFieldValue); var privateMethod = targetClazz.getDeclaredMethod("privateMethod"); privateMethod.setAccessible(true); privateMethod.invoke(myClass); } catch (NoSuchFieldException | IllegalAccessException | NoSuchMethodException | InvocationTargetException e) { System.err.println("基本リフレクションエラー: " + e.getMessage()); } フィールド情報を取得

Slide 24

Slide 24 text

try { var myClass = new MyClass(); var targetClazz = myClass.getClass(); var privateField = targetClazz.getDeclaredField("privateField"); privateField.setAccessible(true); var privateFieldValue = privateField.get(myClass); System.out.printf("My private field is: %s\n", privateFieldValue); var privateMethod = targetClazz.getDeclaredMethod("privateMethod"); privateMethod.setAccessible(true); privateMethod.invoke(myClass); } catch (NoSuchFieldException | IllegalAccessException | NoSuchMethodException | InvocationTargetException e) { System.err.println("基本リフレクションエラー: " + e.getMessage()); } privateなのでアクセスできるようにする

Slide 25

Slide 25 text

try { var myClass = new MyClass(); var targetClazz = myClass.getClass(); var privateField = targetClazz.getDeclaredField("privateField"); privateField.setAccessible(true); var privateFieldValue = privateField.get(myClass); System.out.printf("My private field is: %s\n", privateFieldValue); var privateMethod = targetClazz.getDeclaredMethod("privateMethod"); privateMethod.setAccessible(true); privateMethod.invoke(myClass); } catch (NoSuchFieldException | IllegalAccessException | NoSuchMethodException | InvocationTargetException e) { System.err.println("基本リフレクションエラー: " + e.getMessage()); } フィールド情報でインスタンスから データを取得

Slide 26

Slide 26 text

try { var myClass = new MyClass(); var targetClazz = myClass.getClass(); var privateField = targetClazz.getDeclaredField("privateField"); privateField.setAccessible(true); var privateFieldValue = privateField.get(myClass); System.out.printf("My private field is: %s\n", privateFieldValue); var privateMethod = targetClazz.getDeclaredMethod("privateMethod"); privateMethod.setAccessible(true); privateMethod.invoke(myClass); } catch (NoSuchFieldException | IllegalAccessException | NoSuchMethodException | InvocationTargetException e) { System.err.println("基本リフレクションエラー: " + e.getMessage()); } private なデータを取得できました

Slide 27

Slide 27 text

try { var myClass = new MyClass(); var targetClazz = myClass.getClass(); var privateField = targetClazz.getDeclaredField("privateField"); privateField.setAccessible(true); var privateFieldValue = privateField.get(myClass); System.out.printf("My private field is: %s\n", privateFieldValue); var privateMethod = targetClazz.getDeclaredMethod("privateMethod"); privateMethod.setAccessible(true); privateMethod.invoke(myClass); } catch (NoSuchFieldException | IllegalAccessException | NoSuchMethodException | InvocationTargetException e) { System.err.println("基本リフレクションエラー: " + e.getMessage()); } メソッドも同様にメソッド情報を取得

Slide 28

Slide 28 text

try { var myClass = new MyClass(); var targetClazz = myClass.getClass(); var privateField = targetClazz.getDeclaredField("privateField"); privateField.setAccessible(true); var privateFieldValue = privateField.get(myClass); System.out.printf("My private field is: %s\n", privateFieldValue); var privateMethod = targetClazz.getDeclaredMethod("privateMethod"); privateMethod.setAccessible(true); privateMethod.invoke(myClass); } catch (NoSuchFieldException | IllegalAccessException | NoSuchMethodException | InvocationTargetException e) { System.err.println("基本リフレクションエラー: " + e.getMessage()); } 触れるようにして

Slide 29

Slide 29 text

try { var myClass = new MyClass(); var targetClazz = myClass.getClass(); var privateField = targetClazz.getDeclaredField("privateField"); privateField.setAccessible(true); var privateFieldValue = privateField.get(myClass); System.out.printf("My private field is: %s\n", privateFieldValue); var privateMethod = targetClazz.getDeclaredMethod("privateMethod"); privateMethod.setAccessible(true); privateMethod.invoke(myClass); } catch (NoSuchFieldException | IllegalAccessException | NoSuchMethodException | InvocationTargetException e) { System.err.println("基本リフレクションエラー: " + e.getMessage()); } メソッド呼び出し

Slide 30

Slide 30 text

メソッドを呼び出す

Slide 31

Slide 31 text

private int privateCalculate(int a, int b) { int result = a * b + a - b; System.out.println("Private calculation: " + a + " * " + b + " + " + a + " - " + b + " = " + result); return result; } private void privateSetValue(String message, int number) { this.privateMessage = message; this.privateNumber = number; System.out.println("Private values updated: message='" + message + "', number=" + number); } private String privateComplexMethod(String[] words, boolean uppercase) { StringBuilder sb = new StringBuilder(); for (int i = 0; i < words.length; i++) { if (i > 0) sb.append(" "); sb.append(uppercase ? words[i].toUpperCase() : words[i].toLowerCase()); } String result = sb.toString(); System.out.println("Complex method processing: " + result); return result; } private static String privateStaticMethod(double value) { String formatted = String.format("%.2f", value); System.out.println("Static method processing: " + formatted); return "Processed: " + formatted; } いろんなメソッド用意しました

Slide 32

Slide 32 text

var privateCalculateMethod = targetClazz.getDeclaredMethod( "privateCalculate", int.class, int.class); privateCalculateMethod.setAccessible(true); var result = privateCalculateMethod.invoke(myClass, 10, 5); System.out.printf("Private calculation result: %s\n", result);

Slide 33

Slide 33 text

var privateCalculateMethod = targetClazz.getDeclaredMethod( "privateCalculate", int.class, int.class); privateCalculateMethod.setAccessible(true); var result = privateCalculateMethod.invoke(myClass, 10, 5); System.out.printf("Private calculation result: %s\n", result); privateCalculate(int foo, int bar)を指定

Slide 34

Slide 34 text

var privateCalculateMethod = targetClazz.getDeclaredMethod( "privateCalculate", int.class, int.class); privateCalculateMethod.setAccessible(true); var result = privateCalculateMethod.invoke(myClass, 10, 5); System.out.printf("Private calculation result: %s\n", result); 呼び出し時に引数を渡す

Slide 35

Slide 35 text

var privateCalculateMethod = targetClazz.getDeclaredMethod( "privateCalculate", int.class, int.class); privateCalculateMethod.setAccessible(true); var result = privateCalculateMethod.invoke(myClass, 10, 5); System.out.printf("Private calculation result: %s\n", result); 戻り値も受け取れる

Slide 36

Slide 36 text

var complexMethod = targetClazz.getDeclaredMethod( "privateComplexMethod", String[].class, boolean.class); complexMethod.setAccessible(true); String[] args = {"Hello", "World", "Java"}; var complexResult = complexMethod.invoke(myClass, args, true); System.out.printf("Complex method result: %s\n", complexResult);

Slide 37

Slide 37 text

var complexMethod = targetClazz.getDeclaredMethod( "privateComplexMethod", String[].class, boolean.class); complexMethod.setAccessible(true); String[] args = {"Hello", "World", "Java"}; var complexResult = complexMethod.invoke(myClass, args, true); System.out.printf("Complex method result: %s\n", complexResult); ちょっと複雑な引数も

Slide 38

Slide 38 text

var complexMethod = targetClazz.getDeclaredMethod( "privateComplexMethod", String[].class, boolean.class); complexMethod.setAccessible(true); String[] args = {"Hello", "World", "Java"}; var complexResult = complexMethod.invoke(myClass, args, true); System.out.printf("Complex method result: %s\n", complexResult); 同じように呼び出せます

Slide 39

Slide 39 text

var staticMethod = targetClazz.getDeclaredMethod("privateStaticMethod", double.class); staticMethod.setAccessible(true); var staticResult = staticMethod.invoke(null, 3.14159); System.out.printf("Static method result: %s\n", staticResult);

Slide 40

Slide 40 text

var staticMethod = targetClazz.getDeclaredMethod("privateStaticMethod", double.class); staticMethod.setAccessible(true); var staticResult = staticMethod.invoke(null, 3.14159); System.out.printf("Static method result: %s\n", staticResult); static メソッドであっても

Slide 41

Slide 41 text

var staticMethod = targetClazz.getDeclaredMethod("privateStaticMethod", double.class); staticMethod.setAccessible(true); var staticResult = staticMethod.invoke(null, 3.14159); System.out.printf("Static method result: %s\n", staticResult); 基本は一緒 インスタンスがないから null を渡す

Slide 42

Slide 42 text

コンストラクタを呼び出す

Slide 43

Slide 43 text

public class SecureClass { private String secretData; private final String FINAL_VALUE = "変更不可"; public SecureClass(String data) { this.secretData = data; } private SecureClass() { this.secretData = "デフォルト値"; } @Override public String toString() { return "SecureClass{secretData='" + secretData + "'}"; } }

Slide 44

Slide 44 text

public class SecureClass { private String secretData; private final String FINAL_VALUE = "変更不可"; public SecureClass(String data) { this.secretData = data; } private SecureClass() { this.secretData = "デフォルト値"; } @Override public String toString() { return "SecureClass{secretData='" + secretData + "'}"; } } 恥ずかしがりなコンストラクタ

Slide 45

Slide 45 text

Constructor privateConstructor = SecureClass.class.getDeclaredConstructor(); privateConstructor.setAccessible(true); SecureClass instance = privateConstructor.newInstance(); System.out.println("private コンストラクタから作成されたインスタンス: " + instance); public class SecureClass { private String secretData; private final String FINAL_VALUE = "変更不可"; public SecureClass(String data) { this.secretData = data; } private SecureClass() { this.secretData = "デフォルト値"; } @Override public String toString() { return "SecureClass{secretData='" + secretData + "'}"; } }

Slide 46

Slide 46 text

Constructor privateConstructor = SecureClass.class.getDeclaredConstructor(); privateConstructor.setAccessible(true); SecureClass instance = privateConstructor.newInstance(); System.out.println("private コンストラクタから作成されたインスタンス: " + instance); private なコンストラクタを取得 public class SecureClass { private String secretData; private final String FINAL_VALUE = "変更不可"; public SecureClass(String data) { this.secretData = data; } private SecureClass() { this.secretData = "デフォルト値"; } @Override public String toString() { return "SecureClass{secretData='" + secretData + "'}"; } }

Slide 47

Slide 47 text

Constructor privateConstructor = SecureClass.class.getDeclaredConstructor(); privateConstructor.setAccessible(true); SecureClass instance = privateConstructor.newInstance(); System.out.println("private コンストラクタから作成されたインスタンス: " + instance); インスタンス生成 public class SecureClass { private String secretData; private final String FINAL_VALUE = "変更不可"; public SecureClass(String data) { this.secretData = data; } private SecureClass() { this.secretData = "デフォルト値"; } @Override public String toString() { return "SecureClass{secretData='" + secretData + "'}"; } }

Slide 48

Slide 48 text

48 ● ねじ曲げたルール ○ カプセル化 ここまでの流れ

Slide 49

Slide 49 text

変更禁止を破る

Slide 50

Slide 50 text

public class SecureClass { private String secretData; private final String FINAL_VALUE = "変更不可"; public SecureClass(String data) { this.secretData = data; } private SecureClass() { this.secretData = "デフォルト値"; } @Override public String toString() { return "SecureClass{secretData='" + secretData + "'}"; } }

Slide 51

Slide 51 text

public class SecureClass { private String secretData; private final String FINAL_VALUE = "変更不可"; public SecureClass(String data) { this.secretData = data; } private SecureClass() { this.secretData = "デフォルト値"; } @Override public String toString() { return "SecureClass{secretData='" + secretData + "'}"; } } 再代入不可

Slide 52

Slide 52 text

SecureClass instance = new SecureClass("テスト"); Field finalField = SecureClass.class.getDeclaredField("FINAL_VALUE"); finalField.setAccessible(true); try { finalField.set(instance, "変更された値"); System.out.println("final フィールドの値を変更しました: " + finalField.get(instance)); instance.printFinalValue();

Slide 53

Slide 53 text

SecureClass instance = new SecureClass("テスト"); Field finalField = SecureClass.class.getDeclaredField("FINAL_VALUE"); finalField.setAccessible(true); try { finalField.set(instance, "変更された値"); System.out.println("final フィールドの値を変更しました: " + finalField.get(instance)); instance.printFinalValue(); いつも通りに取得

Slide 54

Slide 54 text

SecureClass instance = new SecureClass("テスト"); Field finalField = SecureClass.class.getDeclaredField("FINAL_VALUE"); finalField.setAccessible(true); try { finalField.set(instance, "変更された値"); System.out.println("final フィールドの値を変更しました: " + finalField.get(instance)); instance.printFinalValue(); 変更禁止? そんなの関係ネェ!!

Slide 55

Slide 55 text

SecureClass instance = new SecureClass("テスト"); Field finalField = SecureClass.class.getDeclaredField("FINAL_VALUE"); finalField.setAccessible(true); try { finalField.set(instance, "変更された値"); System.out.println("final フィールドの値を変更しました: " + finalField.get(instance)); instance.printFinalValue(); いつも通りに取得

Slide 56

Slide 56 text

SecureClass instance = new SecureClass("テスト"); Field finalField = SecureClass.class.getDeclaredField("FINAL_VALUE"); finalField.setAccessible(true); try { finalField.set(instance, "変更された値"); System.out.println("final フィールドの値を変更しました: " + finalField.get(instance)); instance.printFinalValue(); ↓実行結果

Slide 57

Slide 57 text

SecureClass instance = new SecureClass("テスト"); Field finalField = SecureClass.class.getDeclaredField("FINAL_VALUE"); finalField.setAccessible(true); try { finalField.set(instance, "変更された値"); System.out.println("final フィールドの値を変更しました: " + finalField.get(instance)); instance.printFinalValue(); ↓実行結果 ???

Slide 58

Slide 58 text

public class SecureClass { private String secretData; private final String FINAL_VALUE = "変更不可"; public SecureClass(String data) { this.secretData = data; } private SecureClass() { this.secretData = "デフォルト値"; } @Override public String toString() { return "SecureClass{secretData='" + secretData + "'}"; } public void printFinalValue() { System.out.println("FINAL_VALUE: " + FINAL_VALUE); } }

Slide 59

Slide 59 text

public class SecureClass { private String secretData; private final String FINAL_VALUE = "変更不可"; public SecureClass(String data) { this.secretData = data; } private SecureClass() { this.secretData = "デフォルト値"; } @Override public String toString() { return "SecureClass{secretData='" + secretData + "'}"; } public void printFinalValue() { System.out.println("FINAL_VALUE: " + FINAL_VALUE); } } 実際の中身を 見てみると?

Slide 60

Slide 60 text

public class SecureClass { private String secretData; private final String FINAL_VALUE = "変更不可"; public SecureClass(String data) { this.secretData = data; } private SecureClass() { this.secretData = "デフォルト値"; } @Override public String toString() { return "SecureClass{secretData='" + secretData + "'}"; } public void printFinalValue() { System.out.println("FINAL_VALUE: " + FINAL_VALUE); } } ←出力結果

Slide 61

Slide 61 text

デバッグ実行では変わってる

Slide 62

Slide 62 text

public class SecureClass { public final String FINAL_VALUE = "変更不可"; public final String FINAL_VALUE2; private SecureClass() { FINAL_VALUE2 = "変更不可"; } public void printFinalValue() { System.out.println("FINAL_VALUE: " + FINAL_VALUE); } public void printFinalValue2() { System.out.println("FINAL_VALUE2: " + FINAL_VALUE2); } }

Slide 63

Slide 63 text

public class SecureClass { public final String FINAL_VALUE = "変更不可"; public final String FINAL_VALUE2; private SecureClass() { FINAL_VALUE2 = "変更不可"; } public void printFinalValue() { System.out.println("FINAL_VALUE: " + FINAL_VALUE); } public void printFinalValue2() { System.out.println("FINAL_VALUE2: " + FINAL_VALUE2); } } コンストラクタで設定するタイプを追加

Slide 64

Slide 64 text

No content

Slide 65

Slide 65 text

変わってる

Slide 66

Slide 66 text

public class SecureClass { private final String FINAL_VALUE = "変更不可"; private final String FINAL_VALUE2; private final MyClass myClass = new MyClass(); : public void printMyClassName() { System.out.println("MyClass name: " + myClass.name); } }

Slide 67

Slide 67 text

public class SecureClass { private final String FINAL_VALUE = "変更不可"; private final String FINAL_VALUE2; private final MyClass myClass = new MyClass(); : public void printMyClassName() { System.out.println("MyClass name: " + myClass.name); } } クラスのパターンも追加

Slide 68

Slide 68 text

Field finalClass = SecureClass.class.getDeclaredField("myClass"); finalClass.setAccessible(true); var myClass = new MyClass(); myClass.name = "changed"; finalClass.set(instance, myClass); instance.printMyClassName();

Slide 69

Slide 69 text

Field finalClass = SecureClass.class.getDeclaredField("myClass"); finalClass.setAccessible(true); var myClass = new MyClass(); myClass.name = "changed"; finalClass.set(instance, myClass); instance.printMyClassName();

Slide 70

Slide 70 text

70 ● ねじ曲げたルール ○ カプセル化 ○ 再代入不可 ここまでの流れ

Slide 71

Slide 71 text

動的にインスタンスを作る

Slide 72

Slide 72 text

private static DynamicInterface createDynamicImplementation(String result) { return (DynamicInterface) java.lang.reflect.Proxy.newProxyInstance( DynamicInterface.class.getClassLoader(), new Class[]{DynamicInterface.class}, (proxy, method, args) -> { System.out.println("動的実装: " + method.getName() + " が呼び出されました"); if (args != null && args.length > 0) { System.out.println("引数: " + args[0]); } return result; } ); }

Slide 73

Slide 73 text

private static DynamicInterface createDynamicImplementation(String result) { return (DynamicInterface) java.lang.reflect.Proxy.newProxyInstance( DynamicInterface.class.getClassLoader(), new Class[]{DynamicInterface.class}, (proxy, method, args) -> { System.out.println("動的実装: " + method.getName() + " が呼び出されました"); if (args != null && args.length > 0) { System.out.println("引数: " + args[0]); } return result; } ); } 標準ライブラリ

Slide 74

Slide 74 text

private static DynamicInterface createDynamicImplementation(String result) { return (DynamicInterface) java.lang.reflect.Proxy.newProxyInstance( DynamicInterface.class.getClassLoader(), new Class[]{DynamicInterface.class}, (proxy, method, args) -> { System.out.println("動的実装: " + method.getName() + " が呼び出されました"); if (args != null && args.length > 0) { System.out.println("引数: " + args[0]); } return result; } ); } メソッド呼び出しはすべてここに転送

Slide 75

Slide 75 text

DynamicInterface dynamic = createDynamicImplementation("動的に渡された戻り値"); String result = dynamic.dynamicMethod("動的生成されたメソッドの呼び出し"); System.out.println("動的に生成されたクラス: " + dynamic.getClass().getName()); System.out.println("実装されたインターフェース: " + java.util.Arrays.toString(dynamic.getClass().getInterfaces())); System.out.println("戻り値: " + result);

Slide 76

Slide 76 text

DynamicInterface dynamic = createDynamicImplementation("動的に渡された戻り値"); String result = dynamic.dynamicMethod("動的生成されたメソッドの呼び出し"); System.out.println("動的に生成されたクラス: " + dynamic.getClass().getName()); System.out.println("実装されたインターフェース: " + java.util.Arrays.toString(dynamic.getClass().getInterfaces())); System.out.println("戻り値: " + result); インターフェースに定義されたメソッド呼び出し

Slide 77

Slide 77 text

private static DynamicInterface createDynamicImplementation(String result) { return (DynamicInterface) java.lang.reflect.Proxy.newProxyInstance( DynamicInterface.class.getClassLoader(), new Class[]{DynamicInterface.class}, (proxy, method, args) -> { System.out.println("動的実装: " + method.getName() + " が呼び出されました"); if (args != null && args.length > 0) { System.out.println("引数: " + args[0]); } return result; } ); }

Slide 78

Slide 78 text

private static DynamicInterface createDynamicImplementation(String result) { return (DynamicInterface) java.lang.reflect.Proxy.newProxyInstance( DynamicInterface.class.getClassLoader(), new Class[]{DynamicInterface.class}, (proxy, method, args) -> { System.out.println("動的実装: " + method.getName() + " が呼び出されました"); if (args != null && args.length > 0) { System.out.println("引数: " + args[0]); } return result; } ); }

Slide 79

Slide 79 text

インスタンスの動きを変える

Slide 80

Slide 80 text

private static TargetInterface createEnhancedProxy(TargetClass original) { return (TargetInterface) java.lang.reflect.Proxy.newProxyInstance( TargetInterface.class.getClassLoader(), new Class[]{TargetInterface.class}, (proxy, method, args) -> { switch (method.getName()) { case "originalMethod": System.out.println("プロキシ: 元のメソッドを呼び出し前の処理"); original.originalMethod(); System.out.println("プロキシ: 元のメソッドを呼び出し後の処理"); return null; case "newMethod": System.out.println("プロキシ: 動的に追加されたメソッドの実行"); System.out.println("引数: " + (args != null && args.length > 0 ? args[0] : "なし")); return "動的メソッドの戻り値: " + (args != null && args.length > 0 ? args[0] : ""); default: return method.invoke(original, args); } } ); }

Slide 81

Slide 81 text

private static TargetInterface createEnhancedProxy(TargetClass original) { return (TargetInterface) java.lang.reflect.Proxy.newProxyInstance( TargetInterface.class.getClassLoader(), new Class[]{TargetInterface.class}, (proxy, method, args) -> { switch (method.getName()) { case "originalMethod": System.out.println("プロキシ: 元のメソッドを呼び出し前の処理"); original.originalMethod(); System.out.println("プロキシ: 元のメソッドを呼び出し後の処理"); return null; case "newMethod": System.out.println("プロキシ: 動的に追加されたメソッドの実行"); System.out.println("引数: " + (args != null && args.length > 0 ? args[0] : "なし")); return "動的メソッドの戻り値: " + (args != null && args.length > 0 ? args[0] : ""); default: return method.invoke(original, args); } } ); } メソッド追加

Slide 82

Slide 82 text

TargetInterface enhanced = createEnhancedProxy(original); enhanced.originalMethod(); enhanced.newMethod("動的に追加されたメソッド");

Slide 83

Slide 83 text

TargetInterface enhanced = createEnhancedProxy(original); enhanced.originalMethod(); enhanced.newMethod("動的に追加されたメソッド");

Slide 84

Slide 84 text

TargetInterface enhanced = createEnhancedProxy(original); enhanced.originalMethod(); enhanced.newMethod("動的に追加されたメソッド");

Slide 85

Slide 85 text

85 ● ねじ曲げたルール ○ カプセル化 ○ 再代入不可 ○ クラスは定義されたとおりに動く ここまでの流れ

Slide 86

Slide 86 text

はじめに 禁断の操作!? 得られるもの まとめ 禁断の操作とその代償から学ぶメタプログラミング入門

Slide 87

Slide 87 text

87 ● シリアライゼーション たとえばなにができるのか オブジェクトとJSON変換

Slide 88

Slide 88 text

88 ● Lombok たとえばなにができるのか @Data で getter/setter 自動生成

Slide 89

Slide 89 text

89 ● Mockito たとえばなにができるのか @Mock でテストダブル作成

Slide 90

Slide 90 text

90 ● Spring Data JPA たとえばなにができるのか リポジトリインターフェースから実装自動生成

Slide 91

Slide 91 text

@RestController // ← リフレクション public class UserController { @Autowired // ← 動的プロキシ UserService userService; @GetMapping("/users/{id}") // ← リフレクション public User getUser(@PathVariable Long id) { // ← リフレクション return userService.findById(id); // ← 動的プロキシ } }

Slide 92

Slide 92 text

はじめに 禁断の操作!? 得られるもの まとめ 禁断の操作とその代償から学ぶメタプログラミング入門

Slide 93

Slide 93 text

93 ● メタプログラミングは諸刃の剣 まとめ 用法用量守って正しく使いましょう!

Slide 94

Slide 94 text

95 ● X ○ @nrslib ● HomePage ○ https://nrslib.com/ ● YouTube ○ https://www.youtube.com/@nrslib おしまい