it-roy-ru.com

Переопределение метода в экземпляре Java-объекта

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

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

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

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

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

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

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

28
Tyler Szabo

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

19
Michael Borgwardt

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

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

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

12
John

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

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

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

6
Jon Skeet

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

Некоторый очень похожий код к требованию, которое у вас есть с сокетами, здесь:

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

1
Pablojim

Если бы 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
Jose Martinez

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

0
sjobe

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

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

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

0
Colin Hebert

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

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

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