タイプキャストは、ダイナミックプロキシとJavaジェネリックで静かに失敗しました

StackOverflow https://stackoverflow.com/questions/19849208

質問

Imyは、ダイナミックプロキシとJavaジェネリックを使用したタイプキャストの動作に関するものです。より具体的には、動的プロキシを使用してServeletオブジェクトを機器にしたいと思います。再利用性を向上させるために、以下のようにプロキシオブジェクトを作成するための一般的なコードテンプレートを採用しました。

@SuppressWarnings("unchecked")
public static <T> T instrument(final T aT) {
    T proxy = (T) Proxy.newProxyInstance(aT.getClass().getClassLoader(), new Class[]{MyServelet.class}, new InvocationHandler() {
        @Override
        public Object invoke(Object aProxy, Method aMethod, Object[] aArgs) throws Throwable {
        ..... instrumentation logic goes here
        }
    });
     System.out.println("proxy class " + proxy.getClass());
     System.out.println("input class " + aT.getClass());
     return proxy;
}

ここでは、MyServeletインターフェイスを実装する私のコンクリートServelet実装クラスです。ただし、上記の方法を実行すると印刷します

proxy class class com.sun.proxy.$Proxy0
input class class MyServeletImplementation

だから、コードスニペットのタイプキャストステートメントはどうなったのだろうか。プロキシがMyServeletimplementationにキャストされなかったため、静かに失敗したようですが、ClassCastExceptionも投げませんでした。誰かが私にこれに光を当てることができますか?ありがとう!

役に立ちましたか?

解決

メソッドの実際のコンパイルされたバイトコードでは、キャストは行われませんでした。 タイプ消去.

ByteCodeを生成するとき、コンパイラはパラメータータイプの変数をそのパラメータータイプの上限と同じタイプと扱います。 Object タイプがバウンドされていない場合。だから T あなたの方法には制約がありました <T extends MyServelet>, 、コンパイラが扱っていたでしょう proxy タイプの変数として MyServelet, 、キャストを挿入しました。ただし、As T バウンドされていない、それはタイプの変数として扱われます Object - したがって、キャストはありません。

他のヒント

動的プロキシのポイントは、返されているプロキシの実際のクラスを気にしないことです。あなたはそれが望ましいすべての指定されたインターフェイスを実装することを気にします。

Javadocsの newProxyInstance 州:

戻り値:

指定されたクラスローダーによって定義され、指定されたインターフェイスを実装するプロキシクラスの指定された呼び出しハンドラーを備えたプロキシインスタンス

そう、 newProxyInstance のインスタンスを返しました com.sun.proxy.$Proxy0 (それが何であれ、私たちは本当に気にしません)、それはそれが MyServelet. 。キャストできるはずです MyServelet なしで ClassCastException, 、 なぜなら newProxyInstance そのインターフェイスを実装するためにクラスを作成し、渡すことでそれが必要であることを指定しました new Class[]{MyServelet.class}.

失敗しなかったタイプキャストのために、Javaジェネリックはそれを推測したに違いありません TMyServelet, 、 いいえ MyServeletImplementation. 。おそらくあなたが電話したとき instrument, 、あなたはこのようにそうしました:

MyServelet proxy = YourClass.instrument(new MyServeletImplementation());

だからキャストはそうでした MyServelet 成功するはずであり、そうではありません MyServeletImplementation, 、失敗するはずです。

追加する必要がある理由 @SuppressWarnings("unchecked") コンパイラがあなたが何か間違ったことをしていることを警告しようとしていたからです。抑制を追加することで、あなたは自分が何をしているのか知っていたので気にしないとコンパイラーに伝えました。そもそも警告を受けていた理由を理解しなかった場合、アノテーションを追加すべきではないでしょうか?

ヒント:ジェネリックキャスト T proxy = (T) に変わります Object proxy = (Object) コンパイラによって。 (別の答えに記載されているように、「タイプ消去」)。

ライセンス: CC-BY-SA帰属
所属していません StackOverflow
scroll top