webentwicklung-frage-antwort-db.com.de

Überschreiben einer Methode in einem instantiierten Java-Objekt

Ich möchte eine Methode in einem Objekt überschreiben, das mir von einer Fabrik übergeben wird, über die ich wenig Kontrolle habe. 

Mein spezielles Problem ist, dass ich das getInputStream und getOutputStream eines Socket-Objekts überschreiben möchte, um Wire Logging auszuführen.

Das generische Problem ist wie folgt:

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

Wo ich eine instanziierte Foo nehmen und die doBar durch meine eigene ersetzen möchte, würde das wie folgt funktionieren:

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

Für den Socket werde ich ein InputStream und OutputStream zurückgeben, die durch Protokollierung umbrochen werden, um die Daten abzufangen.

28
Tyler Szabo

Da Java klassenbasierte OO verwendet, ist dies nicht möglich. Sie können das Muster decorator verwenden, d. H. Einen Wrapper für das Objekt schreiben, das die umhüllten Streams zurückgibt.

19

Ich denke, es gibt einen Weg, den gewünschten Effekt zu erzielen. Ich habe gesehen, dass er ursprünglich in Swing mit Tasten verwendet wurde, damit der Programmierer die Taste dazu bringen kann, etwas zu tun, wenn sie angeklickt wird.

Angenommen, Sie haben Ihren Foo-Kurs:

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

Dann hast du eine Läuferklasse oder ähnliches. Sie können die doBar () -Methode zum Zeitpunkt der Instantiierung überschreiben. Dies wirkt sich nur auf das bestimmte Objekt aus.

diese Klasse könnte so aussehen:

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
}

Ich bin mir nicht ganz sicher, ob das den Trick für Sie tun wird, aber vielleicht bringt es Sie in die richtige Richtung. Wenn nichts anderes möglich ist, eine Methode außerhalb der Klasse zu überschreiben, kann dies vielleicht helfen.

12
John

Sie können keine Methoden in vorhandenen Objekten ersetzen - Sie können den Typ eines vorhandenen Objekts nicht ändern.

Sie können eine neue Instanz einer anderen Klasse erstellen, die delegiert an die vorhandene Instanz delegiert, dies hat jedoch auch Einschränkungen.

Gibt es in Ihrem realen Fall keine Möglichkeit, die vom Socket zurückgegebenen Streams einfach einzeln aufzurufen? Kannst du mehr Details geben?.

6
Jon Skeet

Die Verwendung eines Dekorateurs ist der richtige Weg:

Ein sehr ähnlicher Code zu der Anforderung, die Sie mit Sockets haben, ist hier:

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

1
Pablojim

Wenn Socket eine Schnittstelle war, könnten Sie einen dynamischen Proxy erstellen. Unten ist ein Beispiel. Ich füge das hier ein, falls andere Leute dies tun müssen und die Zielinstanz eine Instanz einer Schnittstelle ist.

Der Hauptgrund, warum dies für Socket nicht funktioniert, besteht darin, dass Java.lang.reflect.Proxy.newProxyInstance ein Array von Schnittstellen für das zweite Argument benötigt, sodass Klassen hier nicht funktionieren. Für dieses Beispiel musste ich eine Schnittstelle mit dem Namen ParentInterface erstellen, die nur die drei Druckmethoden enthält.

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

Ich bin mir nicht sicher, ob das möglich ist. Haben Sie darüber nachgedacht, eine eigene Klasse zu erstellen, das Objekt von der Factory als Member zurückgegeben zu bekommen und dann die doBar () - Methode für diese Klasse zu schreiben. 

0
sjobe

Sie können ein Objekt in Java nicht direkt ändern.

Sie könnten etwas haben, das tut, was Sie wollen, indem Sie Ihre Foo in ein anderes ähnliches Objekt packen, das jeden Aufruf an Foo delegiert und gleichzeitig alles protokolliert, was Sie möchten. (siehe Proxy )

Wenn Sie jedoch protokollieren möchten, ist Aspekt vielleicht die bessere Wahl. (siehe Aspekt J )

0
Colin Hebert

zwei Optionen:

  1. einfach: Wenn Sie Foo implementieren, können Sie einen Dynamic-Proxy verwenden, um neue Funktionen hinzuzufügen. 
  2. mehr Arbeit: Was Sie haben, ist eine "um" Beratung von AOP - Sie können jedes der vorhandenen AOP-Tools verwenden, um dies zu ermöglichen. Spring Framework kann dies für Sie tun, wenn Sie es bereits verwenden.
0
kartheek

Eine weitere Proxy-bezogene Lösung: Sie können Aspects verwenden, um eine Methode für ein gegebenes Objekt zu überschreiben, ohne sie selbst zu klassifizieren. Dies ist besonders für die Protokollierung geeignet und üblich. Dieses Beispiel verwendet 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