2016-03-19 4 views
0

これは、「Thinking in Java」の変更された動的プロキシの例です。上記の例でInvokeHandlerのinvoke()メソッド内でproxy.getClass()を呼び出すのはなぜですか?

import java.lang.reflect.*; 

interface Interface { void foo(); } 

class RealObject implements Interface { 
    public void foo() {} 
} 

class DynamicProxyHandler implements InvocationHandler { 
    private Object proxied; 
    public DynamicProxyHandler(Object proxied) { 
     this.proxied = proxied; 
    } 
    @Override 
    public Object 
    invoke(Object proxy, Method method, Object[] args) 
    throws Throwable { 
     proxy.toString(); 
     return method.invoke(proxied, args); 
    } 
} 

public class ProxyTest { 
    public static void main(String args[]) { 
     RealObject real = new RealObject(); 
     Interface proxy = (Interface)Proxy.newProxyInstance(
      Interface.class.getClassLoader(), 
      new Class[]{ Interface.class }, 
      new DynamicProxyHandler(real)); 
     proxy.foo(); 
    } 
} 

、Iはinvoke()方法内側toString()メソッドを呼び出します。私が予想したように、プロキシのtoString()メソッドを呼び出すと再びハンドラが呼び出されるため、無限の再帰が発生します。

これはブルースEckel氏は、「Javaで考える」で述べてどのようである: invoke()、内部プロキシのメソッドを呼び出すときにインターフェイスを介して通話が プロキシ経由でリダイレクトされるので

しかし、注意してください。

例外の詳細:

Exception in thread "main" java.lang.StackOverflowError 
    at DynamicProxyHandler.invoke(ProxyTest.java:19) 
    at $Proxy0.toString(Unknown Source) 
    at DynamicProxyHandler.invoke(ProxyTest.java:19) 
    at $Proxy0.toString(Unknown Source) 
    ... 

しかし、私はproxy.toString();ためproxy.getClass();に置き換えた場合:

public Object 
invoke(Object proxy, Method method, Object[] args) 
throws Throwable { 
    proxy.getClass(); 
    return method.invoke(proxied, args); 
} 

すべてがOKです。 No StackOverflowError。無限再帰はありません。

また、proxy.toString();proxy.hashCode();またはproxy.equals("foo");に置き換えようとしました。 StackOverflowErrorも発生しました。

なぜgetClass()toString(),hashCode()equals()と異なるのですか?

+0

'getClass()'は、 'Obect'クラスによって実装された最終的なメソッドです。上書きすることはできません。 – saka1029

答えて

1

回答は、プロキシクラスのマニュアルに見出すことができる:https://docs.oracle.com/javase/7/docs/api/java/lang/reflect/Proxy.html

ハッシュコードの呼び出し、等しい、またはプロキシインスタンス上のjava.lang.Object上位で宣言されたtoStringメソッドは、符号化とに派遣されます上記のように、インタフェースメソッドの呼び出しと同じ方法で、呼び出しハンドラのinvokeメソッドがエンコードされ、送出されます。 invokeに渡されたMethodオブジェクトの宣言クラスはjava.lang.Objectになります。 java.lang.Objectから継承されたプロキシインスタンスのその他のパブリックメソッドは、プロキシクラスによってオーバーライドされないため、これらのメソッドの呼び出しはjava.lang.Objectのインスタンスの場合と同様に動作します。

また、私はそれがtoString()方法は、デフォルトの定義を持っているという事実とは何かがあると思う:

:(など、および wait() notify()getClass()ながら

public String toString() { 
    return getClass().getName() + "@" + Integer.toHexString(hashCode()); 
} 

は以下のように定義されています

public final native Class<?> getClass(); 

したがって、どのメソッドがプロキシされないかを区別するために、メソッド定義内にfinal nativeの存在を見つけることができますn。

関連する問題