Wrapper classes are not suitable for callback structures

The disadvantages of shell classes are few. One caveat is that wrapper classes are not suitable for use in callback infrastructures, where objects pass their own references to other objects for subsequent calls (β€œcallbacks”). Since the wrapped object does not know about its shell, it passes a reference to itself (this), and callbacks elude the shell.

Can someone explain what this means, for example, with an example. It is written in Effective Java, but I did not quite understand it.

To add to the context instead of inheritance, we must approve a composition that leads instead to a subclass of Set , we should use something like this:

 public class ForwardingSet<E> implements Set<E> { private final Set<E> s; public ForwardingSet(Set<E> s) { this.s = s; } public void clear() { s.clear(); } public boolean contains(Object o) { return s.contains(o); } ... } 

But, as this fails, I am still not able to understand callbacks. In JavaScript, we can use function callbacks, but how the same concept applies to Java, if someone can explain.

+9
source share
2 answers

If you can guarantee that you always go anywhere (for future callbacks) a link to the object that is being sent, then everything is in order. Nevertheless, you can create an object, wrap it with some class, but this object itself can have some method that passes this somewhere, for example, to some kind of listener or somewhere else. In this case, your cover has no idea what is happening with the wrapped object. For instance:.

 // basic class which we will wrap public class Model{ Controller controller; Model(Controller controller){ this.controller = controller; controller.register(this); //Pass SELF reference } public void makeChange(){ ... } } public class Controller{ private final Model model; public void register(Model model){ this.model = model; } // Here the wrapper just fails to count changes, // because it does not know about the wrapped object // references leaked public void doChanges(){ model.makeChange(); } } // wrapper class public class ModelChangesCounter{ private final Model; private int changesMade; ModelWrapper(Model model){ this.model = model; } // The wrapper is intended to count changes, // but those changes which are invoked from // Controller are just skipped public void makeChange(){ model.makeChange(); changesMade++; } } 

The wrapper for Model just eludes the makeChange() method calls that come from the Controller callback.

+8
source
  interface SomethingWithCallback { void doSomething(); void call(); } class WrappedObject implements SomethingWithCallback { private final SomeService service; WrappedObject(SomeService service) { this.service = service; } @Override public void doSomething() { service.performAsync(this); } @Override public void call() { System.out.println("WrappedObject callback!"); } } class Wrapper implements SomethingWithCallback { private final WrappedObject wrappedObject; Wrapper(WrappedObject wrappedObject) { this.wrappedObject = wrappedObject; } @Override public void doSomething() { wrappedObject.doSomething(); } void doSomethingElse() { System.out.println("We can do everything the wrapped object can, and more!"); } @Override public void call() { System.out.println("Wrapper callback!"); } } final class SomeService { void performAsync(SomethingWithCallback callback) { new Thread(() -> { perform(); callback.call(); }).start(); } void perform() { System.out.println("Service is being performed."); } } public static void main(String[] args) { SomeService service = new SomeService(); WrappedObject wrappedObject = new WrappedObject(service); Wrapper wrapper = new Wrapper(wrappedObject); wrapper.doSomething(); } 

The problem is that although we called doSomething () on the shell, the wrapped object's callback was called, not the shell's callback. This is what Joshua Bloch refers to when he says that "callbacks elude the shell."

Link: Link

0
source

Source: https://habr.com/ru/post/981810/


All Articles