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

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

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

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

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

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

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

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

Ответы на вопрос(9)

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

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

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

 Rob Newton05 сент. 2018 г., 08:26
Вы переопределяете метод при создании объекта. Но вопрос гласит, что он передал объект с фабрики, поэтому объект уже создан. Вы не можете переопределить метод объекта, когда объект уже создан.
 Hazem Elraffiee18 авг. 2013 г., 11:15
Оба сделают оригинальную деятельность. Переопределение с использованием анонимных классов работает только для методов внутри самого объекта. Для вызывающей стороны (вне объекта) вы не можете использовать переопределенную версию метода.
 Leo Pflug06 февр. 2014 г., 12:04
Я на самом деле только что проверил, и это работает. Не знаю, о чем ты говоришь, Хазем.

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

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

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

Решение Вопроса

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

 Jeremy Trifilo22 июл. 2014 г., 17:39
@Erhannis Reflection - ужасная идея, особенно для чего-то, связанного с сетью / данными.
 Erhannis20 июн. 2014 г., 20:52
@YAT Вы можете посмотреть на отражение. Обычно это считается плохим тоном и довольно хакерским, но это может сработать. Подобно:Method[] methods = object.getClass().getDeclaredMethods(); methods[0].setAccessible(true); methods[0].invoke(object, arg1, arg2);
 Tyler Szabo08 окт. 2010 г., 03:10
Украшение сокета - это настоящая боль, но, похоже, это мой единственный вариант. Благодарю.
 Tourki06 окт. 2013 г., 19:46
Привет, я пытался с декоратором, но на самом деле у объекта, который я пытаюсь украсить, есть некоторые защищенные методы, которые он использует внутренне, я не могу переопределить эти методы, потому что мне пришлось бы вызывать их из экземпляра моего объекта, что невозможно, потому что они защищенный

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

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

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

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

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

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

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

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

Если бы 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");
        }

    }

}

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

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

Ваш ответ на вопрос