Переопределение метода в экземпляре Java-объекта - PullRequest
31 голосов
/ 06 октября 2010

Я хотел бы переопределить метод в объекте, который передал мне фабрика, над которой я мало что могу контролировать.

Моя конкретная проблема заключается в том, что я хочу переопределить getInputStream и getOutputStream объекта Socket для выполнения регистрации проводов .

Общая проблема заключается в следующем:

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

Где бы я хотел взять экземплярный экземпляр Foo и заменить doBar своим собственным, который будет работать следующим образом:

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

Для сокета я собираюсь вернуть InputStream и OutputStream , которые упакованы в журналы для перехвата данных.

Ответы [ 9 ]

20 голосов
/ 06 октября 2010

Поскольку Java использует OO на основе классов, это невозможно.Что вы можете сделать, это использовать шаблон декоратора , т.е. написать оболочку для объекта, который возвращает упакованные потоки.

13 голосов
/ 27 июня 2012

Я думаю, что есть способ достичь желаемого эффекта. Я видел, как она обычно использовалась в свинге с кнопками, чтобы позволить программисту заставить кнопку что-то делать при нажатии.

Скажем, у вас есть класс 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
}

Я не совсем уверен, что это поможет вам, но, возможно, это укажет вам правильное направление. Если ничто иное не возможно переопределить метод вне класса, может быть, это поможет вам.

6 голосов
/ 06 октября 2010

Вы не можете заменить методы в существующих объектах - вы не можете изменить тип существующего объекта, с одной стороны.

Вы можете создать новый экземпляр другого класса, который делегировал существующему экземпляру, но у него тоже есть ограничения.

В вашем случае с реальным миром нет способа просто сделать отдельный вызов, чтобы обернуть потоки, возвращаемые сокетом? Можете ли вы дать более подробную информацию.

1 голос
/ 06 октября 2010

Использование декоратора - правильный путь:

Здесь приведен код, очень похожий на то, которое вы предъявляете к требованиям, предъявляемым к сокетам:

0 голосов
/ 28 декабря 2016

Если бы Socket был интерфейсом, то вы могли бы создать динамический прокси.Ниже приведен пример.Я помещаю это здесь на случай, если другие люди должны сделать это, и целевой экземпляр является экземпляром интерфейса.

Основная причина, по которой это не будет работать для Socket, заключается в том, что для java.lang.reflect.Proxy.newProxyInstance требуется массив интерфейсов для егоВторой аргумент, поэтому классы не будут работать здесь.Для этого примера мне пришлось создать интерфейс с именем ParentInterface, в котором есть только три метода печати. ​​

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 голосов
/ 09 декабря 2016

Другое решение, связанное с прокси: вы можете использовать Аспекты, чтобы переопределить метод для данного объекта, не помещая его в подкласс.Это особенно уместно и распространено для регистрации.В этом примере используется 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 голосов
/ 06 октября 2010

два варианта:

  1. просто: если вы используете интерфейс Foo, вы можете использовать Динамический прокси для добавления новых функций.
  2. больше работы: у вас есть "обходной" совет АОП - вы можете использовать любой из существующих инструментов АОП, чтобы сделать это возможным.Spring Framework может сделать это за вас, если вы уже используете его.
0 голосов
/ 06 октября 2010

Вы не можете реально изменить объект на лету в Java.

Вы можете иметь что-то, что делает то, что вы хотите, обернув ваш Foo в другой подобный объект, который будет делегировать каждый вызов Foo и в тот же журнал все, что вы хотите. (см. Proxy)

Но если вы хотите заняться ведением журнала, возможно, аспект является лучшим выбором. (см. AspectJ )

0 голосов
/ 06 октября 2010

Я не уверен, возможно ли это. Рассматривали ли вы создание собственного класса, возвращение фабрикой объекта в качестве члена, а затем написание метода doBar () для этого класса.

...