モジュール java.base
パッケージ java.lang.reflect

クラスProxy

java.lang.Object
java.lang.reflect.Proxy
すべての実装されたインタフェース:
Serializable

public class Proxy extends Object implements Serializable
Proxyは、インタフェースのインスタンスと同様に動作するが、カスタマイズされたメソッド呼出しを可能にするオブジェクトを作成するための静的メソッドを提供します。 インタフェースFooのプロキシ・インスタンスを作成するには:

     InvocationHandler handler = new MyInvocationHandler(...);
     Foo f = (Foo) Proxy.newProxyInstance(Foo.class.getClassLoader(),
                                          new Class<?>[] { Foo.class },
                                          handler);
 

「プロキシ・クラス」は、指定されたインタフェースのリスト(「プロキシ・インタフェース」)を実装する実行時に作成されるクラスです。 プロキシ・インスタンスは、プロキシ・クラスのインスタンスです。 各プロキシ・インスタンスには関連付けられた呼出しハンドラ・オブジェクトがあり、それにはインタフェースInvocationHandlerが実装されています。 プロキシ・インタフェースの1つを使ったプロキシ・インスタンスでのメソッド呼出しは、インスタンスの呼出しハンドラのinvokeメソッドにディスパッチされ、呼び出されたメソッドを識別するjava.lang.reflect.Methodオブジェクト、および引数を格納するObject型の配列をプロキシ・インスタンスに引き渡します。 呼出しハンドラは符号化されたメソッド呼出しを適切に処理し、呼出しハンドラが返す結果が、プロキシ・インスタンスでのメソッド呼出しの結果として返されます。

プロキシ・クラスには次のプロパティがあります。

  • プロキシ・クラスの修飾されていない名前は指定されません。 ただし文字列「$Proxy」で始まるクラス名の領域をプロキシ・クラスのために確保しておく必要があります。
  • プロキシ・クラスが定義されているパッケージおよびモジュールは、belowで指定します。
  • プロキシ・クラスは「最終的かつ非抽象的」です。
  • プロキシ・クラスはjava.lang.reflect.Proxyを継承します。
  • プロキシ・クラスはその生成時に指定されたとおりのインタフェースを、同じ順序で実装します。 ClassオブジェクトでgetInterfacesを呼び出すと、同じインタフェース(その作成時に指定された順序で)のリストを含む配列が返され、そのClassオブジェクトでgetMethodsを呼び出すと、それらのインタフェース内のすべてのメソッドを含むMethodオブジェクトの配列が返され、getMethodを呼び出すと、想定どおりにプロキシ・インタフェース内のメソッドが検索されます。
  • プロキシ・クラスのProtectionDomainは、java.lang.Objectなどのブートストラップ・クラス・ローダーによってロードされるシステム・クラスのProtectionDomainと同じです。これは、プロキシ・クラスのコードが信頼できるシステム・コードによって生成されるためです。 標準では、この保護ドメインに対してjava.security.AllPermissionが与えられます。
  • Proxy.isProxyClassメソッドを使用して、特定のクラスがプロキシ・クラスであるかどうかを判断できます。

プロキシ・インスタンスには次のプロパティがあります。

  • プロキシ・インスタンスproxyとそのプロキシ・クラスによって実装されたインタフェースFooがある場合、次の式はtrueを返します:
         proxy instanceof Foo
     
    また、次のキャスト操作が成功します(ClassCastExceptionをスローする場合を除く)。
         (Foo) proxy
     
  • 各プロキシ・インスタンスには、そのコンストラクタに引き渡された、関連する呼出しハンドラがあります。 static Proxy.getInvocationHandlerメソッドは、その引数として渡されたプロキシ・インスタンスに関連付けられた呼出しハンドラを返します。
  • プロキシ・インスタンスでのインタフェース・メソッドの呼出しは、そのメソッドのドキュメントで述べられているように符号化され、呼出しハンドラのinvokeメソッドにディスパッチされます。
  • プロキシ・インタフェースは、デフォルト・メソッドを定義することも、スーパー・インタフェースから直接的または間接的にデフォルト・メソッドを継承することもできます。 呼出しハンドラは、InvocationHandler::invokeDefaultを呼び出すことで、プロキシ・インタフェースのデフォルト・メソッドを呼び出すことができます。
  • プロキシ・インスタンス上のjava.lang.Objectに宣言されているhashCodeequalsまたはtoStringの呼出しは、前述したようにインタフェース・メソッド呼出しと同じ方法で、エンコードされ、呼出しハンドラのinvokeメソッドにディスパッチされます。 invokeに渡されるMethodオブジェクトの宣言クラスは、java.lang.Objectです。 java.lang.Objectから継承されるプロキシ・インスタンスのその他のpublicメソッドは、プロキシ・クラスによってオーバーライドされません。このため、これらのメソッドの呼出しは、java.lang.Objectのインスタンスに対する呼出しと同様に行われます。

Proxyクラスのパッケージとモジュールのメンバーシップ

プロキシ・クラスが属するパッケージおよびモジュールは、プロキシ・クラスのアクセシビリティがプロキシ・インタフェースのアクセシビリティと一致するように選択されます。 具体的には、getProxyClass(ClassLoader, Class[])またはnewProxyInstance(ClassLoader, Class[], InvocationHandler)メソッドで定義されたプロキシ・クラスのパッケージおよびモジュール・メンバーシップを次のように指定します:
  1. すべてのプロキシ・インタフェースがexportedまたはopenパッケージにある場合は、次のようになります:
    1. すべてのプロキシ・インタフェースがpublicの場合、プロキシ・クラスは無条件にエクスポートされたが開いていないパッケージ内のpublicです。 パッケージおよびモジュールの名前が指定されていません。
    2. すべてのプロキシ・インタフェースのうち少なくとも1つがnon-publicの場合、プロキシ・クラスは非パブリック・インタフェースのパッケージおよびモジュール内のnon-publicです。 すべての非パブリック・インタフェースが同じパッケージおよびモジュール内にある必要があります。そうでない場合、プロキシは「ありえない」です。
  2. non-exportedおよびnon-openであるパッケージに少なくとも1つのプロキシ・インタフェースがある場合:
    1. すべてのプロキシ・インタフェースがpublicの場合、動的モジュール。」non-exportednon-openパッケージのプロキシ・クラスはpublicです パッケージとモジュールの名前が指定されていません。
    2. すべてのプロキシ・インタフェースのうち少なくとも1つがnon-publicの場合、プロキシ・クラスは非パブリック・インタフェースのパッケージおよびモジュール内のnon-publicです。 すべての非パブリック・インタフェースが同じパッケージおよびモジュール内にある必要があります。そうでない場合、プロキシは「ありえない」です。

プロキシ・インタフェースが複数の機能を備えている場合 -- たとえば、エクスポートされたパブリック・インタフェースと非エクスポートの非パブリック・インタフェース -- 同じインスタンスによってプロキシされ、プロキシ・クラスのアクセシビリティは最もアクセスしにくいプロキシ・インタフェースによって管理されます。

任意のコードがsetAccessibleを使用してオープン・パッケージ内のプロキシ・クラスにアクセスできるのに対し、非オープン・パッケージ内のプロキシ・クラスはプロキシ・クラスのモジュール外のコードからはアクセスできないことに注意してください。

この仕様全体を通して、"エクスポートされていないパッケージ"は、すべてのモジュールにエクスポートされないパッケージを指し、"非オープン・パッケージ"は、すべてのモジュールにオープンされていないパッケージを指します。 具体的には、これらの用語は、含まれているモジュールによってエクスポート/オープンされないパッケージ、または含まれているモジュールによって修飾された方法でエクスポート/オープンされるパッケージを指します。

動的モジュール

動的モジュールは、実行時に生成される名前付きモジュールです。 動的モジュールで定義されたプロキシ・クラスはカプセル化され、どのモジュールからもアクセスできません。 動的モジュール内のプロキシ・クラスでConstructor.newInstance(Object...)をコールすると、IllegalAccessExceptionがスローされます。かわりにProxy.newProxyInstanceメソッドを使用する必要があります。

動的モジュールは、プロキシ・クラスのすべてのスーパー・インタフェースのモジュール、およびプロキシ・クラスのすべてのパブリック・メソッド・シグネチャによって参照されるクラスとインタフェースのモジュールを読み取ることができます。 スーパー・インタフェースまたは参照クラスまたはインタフェース(Tなど)がエクスポートされていないパッケージ内にある場合、Tmoduleが更新され、Tのパッケージが動的モジュールにエクスポートされます。

複数のプロキシ・インタフェースで重複するメソッド

2つ以上のプロキシ・インタフェースに同じ名前とパラメータ・シグネチャを持つメソッドが含まれている場合、プロキシ・クラスのインタフェースの順序が重要になります。 プロキシ・インスタンス上で重複するメソッドが呼び出された場合、呼出しハンドラに渡されるMethodオブジェクトで、プロキシ・メソッドの呼出しに使用されたインタフェースの参照型から宣言クラスを割り当てることができないことがあります。 このような制約が存在するのは、生成されたプロキシ・クラス内の対応するメソッドの実装から、その実装が呼び出されたときに使用されたインタフェースを特定できないためです。 このため、プロキシ・インスタンス上で重複するメソッドが呼び出された場合は、メソッド呼出しに使用された参照型にかかわりなく、プロキシ・クラスのインタフェース・リストでそのメソッド(直接またはスーパー・インタフェースから継承)を含むインタフェースのうち、最初のインタフェースのメソッドのMethodオブジェクトが呼出しハンドラのinvokeメソッドに渡されます。

プロキシ・インタフェースに、java.lang.ObjecthashCodeequals、またはtoStringメソッドと同じ名前およびパラメータ・シグニチャを持つメソッドが含まれる場合は、プロキシ・インスタンス上でそのメソッドが呼び出されると、呼出しハンドラに渡されるMethodオブジェクトの宣言クラスはjava.lang.Objectになります。 つまり、publicで非finalであるjava.lang.Objectのメソッドは、呼出しハンドラに渡すMethodオブジェクトを決定するときに、論理的にほかのプロキシ・インタフェースより優先されます。

重複するメソッドが呼出しハンドラにディスパッチされた場合は、invokeメソッドからスローできるチェック例外の型は、チェックされる型のうち、呼び出されるすべてのプロキシ・インタフェースのメソッドに指定されている、throws句の例外の型に割り当てることができるものに限定されます。 invokeメソッドが、呼出しに使えるプロキシ・インタフェースの1つのメソッドで宣言された例外タイプのどれにも割当てできないチェック例外をスローした場合、チェックされないUndeclaredThrowableExceptionがプロキシ・インスタンスでの呼出しによってスローされます。 つまり、invokeメソッドに渡されたMethodオブジェクト上で、getExceptionTypesを呼び出して例外の型を取得しても、invokeメソッドから正常にスローされないことがあります。

導入されたバージョン:
1.3
関連項目: