モジュール 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
関連項目:
  • フィールドのサマリー

    フィールド
    修飾子と型
    フィールド
    説明
    このプロキシ・インスタンスの呼出しハンドラです。
  • コンストラクタのサマリー

    コンストラクタ
    修飾子
    コンストラクタ
    説明
    protected
    指定された値で、サブクラス(通常は動的プロキシ・クラス)からその呼出しハンドラに新しいProxyインスタンスを構築します。
  • メソッドのサマリー

    修飾子と型
    メソッド
    説明
    指定されたプロキシ・インスタンスの呼出しハンドラを返します。
    static Class<?>
    getProxyClass(ClassLoader loader, Class<?>... interfaces)
    非推奨。
    名前付きモジュールで生成されたプロキシ・クラスはカプセル化され、モジュール外のコードにはアクセスできません。
    static boolean
    指定されたクラスがプロキシ・クラスである場合にtrueを返します。
    static Object
    newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler h)
    指定された呼出しハンドラにメソッド呼出しをディスパッチする、指定されたインタフェースのプロキシ・インスタンスを返します。

    クラスjava.lang.Objectで宣言されたメソッド

    clone, equals, finalize, getClass, hashCode, notify, notifyAll, toString, wait, wait, wait
  • フィールド詳細

    • h

      protected InvocationHandler h
      このプロキシ・インスタンスの呼出しハンドラです。
  • コンストラクタの詳細

    • Proxy

      protected Proxy(InvocationHandler h)
      指定された値で、サブクラス(通常は動的プロキシ・クラス)からその呼出しハンドラに新しいProxyインスタンスを構築します。
      パラメータ:
      h - このプロキシ・インスタンスの呼出しハンドラ
      例外:
      NullPointerException - 指定された呼び出しハンドラhnullの場合
  • メソッドの詳細

    • getProxyClass

      @Deprecated public static Class<?> getProxyClass(ClassLoader loader, Class<?>... interfaces) throws IllegalArgumentException
      非推奨。
      名前付きモジュールで生成されたプロキシ・クラスはカプセル化され、モジュール外のコードにはアクセスできません。 Constructor.newInstanceは、アクセスできないプロキシ・クラスで呼び出されると、IllegalAccessExceptionをスローします。 代わりにnewProxyInstance(ClassLoader, Class[], InvocationHandler)を使用してプロキシ・インスタンスを作成してください。
      クラス・ローダーとインタフェースの配列の指定されたプロキシ・クラスのjava.lang.Classオブジェクトを返します。 プロキシ・クラスは指定されたクラス・ローダーにより定義され、指定されたインタフェースをすべて実装します。 指定されたインタフェースのいずれかが非publicである場合、プロキシ・クラスは非publicになります。 インタフェースの同じ順列のプロキシ・クラスがすでにクラス・ローダーにより定義されている場合、既存のプロキシ・クラスが返されます。そうでない場合は、これらのインタフェースのプロキシ・クラスが動的に生成され、クラス・ローダーにより定義されます。
      パラメータ:
      loader - プロキシ・クラスを定義するクラス・ローダー
      interfaces - プロキシ・クラスが実装するインタフェースのリスト
      戻り値:
      指定されたクラス・ローダーで定義され、指定されたインタフェースを実装するプロキシ・クラス
      例外:
      IllegalArgumentException - パラメータ上のrestrictionsのいずれかに違反した場合
      SecurityException - セキュリティ・マネージャsが存在し、次の条件のどれかが満たされる場合:
      • 指定されたloadernullで、呼出し側のクラス・ローダーがnullではなく、RuntimePermission("getClassLoader")のアクセス権を使用したs.checkPermissionの呼出しがアクセスを許可しない。
      • 各プロキシ・インタフェースintfについて、呼出し側のクラス・ローダーがintfのクラス・ローダーと同じでもその祖先でもなく、s.checkPackageAccess()の呼出しがintfへのアクセスを許可しない。
      NullPointerException - interfaces配列の引数またはその要素のいずれかがnullの場合
      関連項目:
    • newProxyInstance

      public static Object newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler h)
      指定された呼出しハンドラにメソッド呼出しをディスパッチする、指定されたインタフェースのプロキシ・インスタンスを返します。

      次の制限のいずれかに違反すると、IllegalArgumentExceptionがスローされます:

      • 指定されたinterfaces配列内のすべてのClassオブジェクトは、クラスまたはプリミティブ型ではなく、non-hiddenおよびnon-sealedインタフェースを表す必要があります。
      • interfaces配列の2つの要素が同一のClassオブジェクトを参照することはできない。
      • すべての型のインタフェースは、対応するクラス・ローダーから名前で参照できなければならない。 つまり、クラス・ローダーがcl、各インタフェースがiの場合は、次の式がtrueでなければならない。

        Class.forName(i.getName(), false, cl) == i

      • 指定されたインタフェースのすべてのpublicメソッド・シグネチャによって参照されるすべての型と、そのスーパー・インタフェースによって継承される型は、指定されたクラス・ローダーを介して名前で参照される必要があります。
      • すべての非公開インタフェースは、指定されたクラス・ローダーによって定義される同じパッケージおよびモジュール内に存在する必要があり、非公開インタフェースのモジュールは、すべてのインタフェース・タイプにアクセスできます。そうしないと、定義されているパッケージに関係なく、プロキシ・クラスがすべてのインタフェースを実装できなくなります。
      • 指定されたインタフェースが同じ署名を持つ場合、そのようなメソッドの任意の組に対して
        • 任意のメソッドの戻り値の型がプリミティブ型またはvoidの場合、それらのメソッドはすべて戻り値の型が同じでなければならない
        • そうでない場合、いずれかのメソッドによる戻り値の型は、残りのメソッドによる戻り値の型すべてに対して割当て可能でなければならない
      • プロキシ・クラスを作成するときは、Virtual Machineに定義されているクラスの制限を超えてはならない。 たとえば、VMが、クラスが実装できるインタフェース数を65535に制限している場合は、interfaces配列のサイズは65535を超えてはならない。

      プロキシ・インタフェースは、順番が区別されます。プロキシ・クラスを2回要求したときに、インタフェースの組み合わせが同じで順番が異なる場合は、2つの異なるプロキシ・クラスが作成されます。

      パラメータ:
      loader - プロキシ・クラスを定義するクラス・ローダー
      interfaces - プロキシ・クラスが実装するインタフェースのリスト
      h - メソッド呼出しのディスパッチ先の呼出しハンドラ
      戻り値:
      指定されたクラス・ローダーで定義され、指定されたインタフェースを実装するプロキシ・クラスの、指定された呼出しハンドラを持つプロキシ・インスタンス
      例外:
      IllegalArgumentException - パラメータ上のrestrictionsのいずれかに違反した場合
      SecurityException - セキュリティ・マネージャsが存在し、次の条件のどれかが満たされる場合:
      • 指定されたloadernullで、呼出し側のクラス・ローダーがnullではなく、RuntimePermission("getClassLoader")のアクセス権を使用したs.checkPermissionの呼出しがアクセスを許可しない。
      • 各プロキシ・インタフェースintfについて、呼出し側のクラス・ローダーがintfのクラス・ローダーと同じでもその祖先でもなく、s.checkPackageAccess()の呼出しがintfへのアクセスを許可しない。
      • 指定されたプロキシ・インタフェースのいずれかが非publicであり、呼出し側のクラスが非publicインタフェースと同じ実行時パッケージ内にはなく、ReflectPermission("newProxyInPackage.{package name}")のアクセス権を使用したs.checkPermissionの呼出しがアクセスを許可しない。
      NullPointerException - interfaces配列の引数またはその要素のいずれかがnullの場合、または呼出しハンドラhnullの場合
      関連項目:
    • isProxyClass

      public static boolean isProxyClass(Class<?> cl)
      指定されたクラスがプロキシ・クラスである場合にtrueを返します。
      実装上のノート:
      セキュリティを判定するときにこのメソッドを使用する場合は、信頼性が重要になります。このため、渡されたクラスがProxyを継承しているかどうかを検査してから、追加の検査を行う必要があります。
      パラメータ:
      cl - テストするクラス
      戻り値:
      クラスがプロキシ・クラスの場合はtrue、そうでない場合はfalse
      例外:
      NullPointerException - clnullである場合
    • getInvocationHandler

      public static InvocationHandler getInvocationHandler(Object proxy) throws IllegalArgumentException
      指定されたプロキシ・インスタンスの呼出しハンドラを返します。
      パラメータ:
      proxy - 呼出しハンドラを返すプロキシ・インスタンス
      戻り値:
      プロキシ・インスタンスの呼出しハンドラ
      例外:
      IllegalArgumentException - 引数がプロキシ・インスタンスではない場合
      SecurityException - セキュリティ・マネージャsが存在し、呼出し側のクラス・ローダーが呼出しハンドラのクラス・ローダーと同じでもその祖先でもなく、s.checkPackageAccess()の呼出しが呼出しハンドラのクラスのアクセスを許可しない。