web-dev-qa-db-ja.com

インスタンス化されたJavaオブジェクトのメソッドをオーバーライドする

私はほとんど制御できないファクトリーから私に渡されたオブジェクトのメソッドをオーバーライドしたいと思います。

私の特定の問題は、(SocketObjectgetInputStreamおよびgetOutputStreamをオーバーライドしてwire logging

一般的な問題は次のとおりです。

public class Foo {
    public Bar doBar() {
        // Some activity
    }
}

インスタンス化されたFooを取得し、doBarを次のように動作する自分のものに置き換えたい場合:

Bar doBar() {
    // My own activity
    return original.doBar();
}

ソケットの場合、ロギングによってラップされてデータをインターセプトするInputStreamおよびOutputStreamを返します。

33
Tyler Szabo

Javaはクラスベースのオブジェクト指向を使用するため、これは不可能です。実行できることは デコレータパターン を使用することです。つまり、ラップされたストリームを返すオブジェクトのラッパーを記述します。

20

あなたが望む効果を達成する方法はあると思います。もともとはボタン付きのスイングで使用され、クリックしたときにプログラマがボタンに何かをさせることができるのを見ました。

Fooクラスがあるとします。

public class Foo {
  public Bar doBar() {
    // Some activity
  }
}

次に、ランナークラスまたは類似のクラスがあります。インスタンス化の時点でdoBar()メソッドをオーバーライドすると、その特定のオブジェクトにのみ影響します。

そのクラスは次のようになります。

public class FooInstance{
  Foo F1 = new Foo(){
    public Bar doBar(){
      //new activity
    }
  }

  Foo F2 = new Foo();

  F1.doBar(); //does the new activity
  F2.doBar(); //does the original activity found in the class
}

私はそれがあなたのためのトリックを行うことを完全に確信していませんが、おそらくそれはあなたを正しい方向に設定するでしょう。他にクラス外のメソッドをオーバーライドすることが可能である場合、それが役立つかもしれません。

13
John

既存のオブジェクトのメソッドを置き換えることはできません。たとえば、既存のオブジェクトのタイプを変更することはできません。

既存のインスタンスに対して委任である別のクラスの新しいインスタンスを作成することもできますが、これにも制限があります。

実際のケースでは、ソケットから返されたストリームをラップするために別の呼び出しを行うだけの方法はありませんか?詳細を教えてください。

6
Jon Skeet

デコレータを使用するのが正しい方法です。

ソケットの要件と非常によく似たコードがいくつかあります。

http://www.javaspecialists.eu/archive/Issue058.html

1
Pablojim

ソケットがインターフェースの場合、動的プロキシを作成できます。以下に例を示します。他の人がこれを行う必要があり、ターゲットインスタンスがインターフェイスのインスタンスである場合に備えて、ここに配置します。

これがソケットで機能しない主な理由は、Java.lang.reflect.Proxy.newProxyInstanceは2番目の引数にインターフェースの配列を必要とするため、クラスはここでは機能しません。そのため、この例では、ParentInterfaceというインターフェイスを作成する必要がありました。これには、3つの印刷メソッドしかありません。

public class Parent implements ParentInterface {

    @Override
    public void print1() {
        System.out.println("parent 1");
    }

    @Override
    public void print2() {
        System.out.println("parent 2");
    }

    @Override
    public void print3() {
        System.out.println("parent 3");
    }

    public static void main(String[] args) {
        Parent originalInstance = new Parent();
        ParentInterface proxied = (ParentInterface) Java.lang.reflect.Proxy.newProxyInstance(
                Parent.class.getClassLoader(),
                new Class[]{ParentInterface.class},
                new ParentProxy(originalInstance));

        proxied.print1();
        proxied.print2();
        proxied.print3();

    }

    static class ParentProxy implements InvocationHandler {

        final Object realObject;

        public ParentProxy(Object real) {
            realObject = real;
        }

        @Override
        public Object invoke(Object target, Method m, Object[] args) throws Throwable {
            try {
                if (m.getName().equals("print2")) {
                    print2();
                    return null;
                } else {
                    return m.invoke(realObject, args);
                }
            } catch (Java.lang.reflect.InvocationTargetException e) {
                throw e.getTargetException();
            }
        }

        public void print2() {
            System.out.println("wrapper 2");
        }

    }

}
0
Jose Martinez

これが可能かどうかはわかりません。独自のクラスを作成し、ファクトリによってメンバーとして返されたオブジェクトを持ち、そのクラスのdoBar()メソッドを記述することを検討しましたか?.

0
sjobe

Javaでオブジェクトをその場で変更することはできません。

Fooを別の同様のオブジェクトにラップして、Fooへのすべての呼び出しを委任し、必要なすべてを同じログで委任することで、希望どおりの処理を行うことができます。 ( Proxy を参照)

しかし、ロギングを行いたい場合は、アスペクトがより良い選択かもしれません。 ( AspectJ を参照)

0
Colin Hebert

2つのオプション:

  1. 簡単:Fooがインターフェースを実装した場合、 動的プロキシ を使用して新しい機能を追加できます。
  2. より多くの作業:あなたが持っているものはAOPの「周りの」アドバイスです-あなたはそれを可能にするために既存のAOPツールのどれでも使うことができます。 Spring Frameworkは、すでに使用している場合は、それを実行できます。
0
kartheek

別のプロキシ関連ソリューション:Aspectsを使用して、自分でサブクラス化せずに特定のオブジェクトのメソッドをオーバーライドできます。これはロギングに特に適切で一般的です。この例では、spring-aopを使用しています。

class Example {

    final Foo foo;

    Example(Foo original) {
        AspectJProxyFactory factory = new AspectJProxyFactory();
        factory.setTarget(original);
        factory.addAspect(FooAspect.class);
        foo = (Foo) factory.getProxy();
    }

    @Aspect
    static class FooAspect {

        @Before("execution(Foo.doBar())")
        Object beforeDoBar() {
            // My own activity
        }
    }
0
Lucas Ross