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

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

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

    プロキシ・クラスのパッケージおよびモジュール・メンバーシップ

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

    アクセス権が混在するプロキシ・インタフェース(たとえば、エクスポートされたパブリック・インタフェースと非エクスポートの非パブリック・インタフェース)が同じインスタンスによってプロキシされる場合、プロキシ・クラスのアクセシビリティは、最もアクセスしやすいプロキシ・インタフェースによって制御されます。

    setAccessibleを含むオープンパッケージ内のプロキシクラスへのアクセスは、任意のコードで取得できますが、非オープンパッケージ内のプロキシクラスは、プロキシクラスのモジュール外のコードからはアクセスできません。

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

    動的モジュール

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

    動的モジュールは、プロキシ・クラスのすべてのスーパーインタフェースのモジュール、およびプロキシ・クラスのすべてのpublicメソッド・シグネチャによって参照されるタイプのモジュールを読み取ることができます。 スーパーインタフェースまたは参照される型(Tなど)がエクスポートされないパッケージ内にある場合、Tモジュールが更新され、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
    関連項目:
    InvocationHandler, 直列化された形式
    • フィールドの詳細

      • 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
        非推奨。
        名前付きモジュールで生成されたプロキシ・クラスはカプセル化され、そのモジュール外のコードにはアクセスできません。 アクセスできないプロキシ・クラスでIllegalAccessExceptionがコールされると、Constructor.newInstanceIllegalAccessExceptionをスローします。 かわりに、newProxyInstance(ClassLoader, Class[], InvocationHandler)を使用してプロキシ・インスタンスを作成します。
        クラス・ローダーとインタフェースの配列の指定されたプロキシ・クラスのjava.lang.Classオブジェクトを返します。 プロキシ・クラスは指定されたクラス・ローダーにより定義され、指定されたインタフェースをすべて実装します。 指定されたインタフェースのいずれかが非publicである場合、プロキシ・クラスは非publicになります。 インタフェースの同じ順列のプロキシ・クラスがすでにクラス・ローダーにより定義されている場合、既存のプロキシ・クラスが返されます。そうでない場合は、これらのインタフェースのプロキシ・クラスが動的に生成され、クラス・ローダーにより定義されます。
        パラメータ:
        loader - プロキシ・クラスを定義するクラス・ローダー
        interfaces - プロキシ・クラスが実装するインタフェースのリスト
        戻り値:
        指定されたクラス・ローダーで定義され、指定されたインタフェースを実装するプロキシ・クラス
        例外:
        IllegalArgumentException - パラメータの制限に違反している場合
        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オブジェクトは、クラスまたはプリミティブ型ではなくインタフェースを表す必要があります。
        • 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 - パラメータの制限に違反している場合
        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()の呼出しが呼出しハンドラのクラスのアクセスを許可しない。