1手で書いた包み役
を使わず、同じを実装した包み役のを手で書いた例です。呼び出す側は Greeter 型として扱うだけなので、包み役に差し替えても気づきません。この方法では、包みたいインターフェースとの数だけ同じようなコードを書くことになります。
2同じことを動的プロキシで行う
包み役のクラスを書かずに、Proxy.newProxyInstance で実行時に作らせた例です。出力の前半は手書きの場合と同じ形になります。後半から、プロキシは Greeter ではあるが RealGreeter ではないこと、java.lang.reflect.Proxy を親に持つ別のクラスであることが分かります。
3作られたクラスの中身
実行時に作られたプロキシのクラスを書き出して で見たものです( 25、 なインターフェースの場合)。greet の本体は、を取り出し、Method と引数の配列を積んで InvocationHandler.invoke を呼び、戻り値を String として返すだけです。前後に何をするかは、すべてハンドラの側に書かれます。
public final class jdk.proxy1.$Proxy0 extends java.lang.reflect.Proxy implements demo.PublicGreeter {...public final java.lang.String greet(java.lang.String) throws ;Code:0: aload_01: getfield #19 // Field java/lang/reflect/Proxy.h:Ljava/lang/reflect/InvocationHandler;4: aload_05: getstatic #55 // Field m3:Ljava/lang/reflect/Method;8: iconst_19: anewarray #11 // class java/lang/Object12: dup13: iconst_014: aload_115: aastore16: invokeinterface #25, 4 // InterfaceMethod java/lang/reflect/InvocationHandler.invoke:(Ljava/lang/Object;Ljava/lang/reflect/Method;[Ljava/lang/Object;)Ljava/lang/Object;21: checkcast #82 // class java/lang/String24: areturn
4クラスの名前は条件で変わる
インターフェースの種類を変えて、作られたクラスの名前を出した実測です。public なインターフェースでは jdk.proxy1.$Proxy0、public でないものでは同じの demo.$Proxy1 になり、同じインターフェースで2回作るとクラスは使い回されました。最後の行から、toString の呼び出しもハンドラに届くことが分かります。
jdk.proxy1.$Proxy0 module=module jdk.proxy1 modifiers=public final interfaces=[interface demo.PublicGreeter]demo.$Proxy1 module=unnamed module @4554617c modifiers=final interfaces=[interface demo.PackagePrivateGreeter]jdk.proxy1.$Proxy2 module=module jdk.proxy1 modifiers=public final interfaces=[interface java.lang.Runnable, interface java.lang.Comparable]jdk.proxy1.$Proxy0 module=module jdk.proxy1 modifiers=public final interfaces=[interface demo.PublicGreeter]p1 and p4 share one proxy class? truehandled greethandled toString
5クラスはプロキシにできない
インターフェースの代わりにふつうのクラスを渡すと、IllegalArgumentException になります。下の出力はを捕まえなかった場合の実物で、String のような クラスやを渡しても同じ is not an interface というメッセージでした。インターフェースを持たないクラスを包むには、別の方法が必要になります。
Exception in thread "main" java.lang.IllegalArgumentException: PlainService is not an interfaceat java.base/java.lang.reflect.Proxy$ProxyBuilder.validateProxyInterfaces(Proxy.java:608)at java.base/java.lang.reflect.Proxy$ProxyBuilder.<init>(Proxy.java:560)...at java.base/java.lang.reflect.Proxy.newProxyInstance(Proxy.java:924)at Uncaught.main(Uncaught.java:5)