Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Wrapper Classes are not suited for callback frameworks

The disadvantages of wrapper classes are few. One caveat is that wrapper classes are not suited for use in callback frameworks, wherein objects pass self references to other objects for subsequent invocations (“callbacks”). Because a wrapped object doesn’t know of its wrapper, it passes a reference to itself (this) and callbacks elude the wrapper.

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

To add to the context, instead of inheritance we should favor composition which leads to instead to sub-classing 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, how this will fail, I am still not able to understand for callbacks. In JavaScript we can use function callbacks but how the same concept applies to Java if someone can explain.

like image 608
Subhomoy Sikdar Avatar asked Jan 31 '15 16:01

Subhomoy Sikdar


People also ask

What are wrapper classes used for?

Wrapper classes provide a way to use primitive data types ( int , boolean , etc..) as objects.

What is wrapper class give suitable example?

A Wrapper class is a class whose object wraps or contains primitive data types. When we create an object to a wrapper class, it contains a field and in this field, we can store primitive data types. In other words, we can wrap a primitive value into a wrapper class object.

What are the advantages of wrapper classes in Java?

The primary advantage of Wrapper Classes is that we need Wrapper objects to function with collections which is only possible with the help of Wrapper classes. As the wrapper classes have objects we can store null as a value. We could not store null in variables of primitive datatype.

What is a wrapper class Why wrapper classes are needed?

A Wrapper class is a class which contains the primitive data types (int, char, short, byte, etc). In other words, wrapper classes provide a way to use primitive data types (int, char, short, byte, etc) as objects.


2 Answers

If you can guarantee that you always pass anywhere(for future callbacks) a reference of an object that is forwarded, then everything is ok. Nevertheless you can create an object, wrap it with some class, but that object itself can have some method that passes this somewhere, e.g to some listener, or somewhere else. In this case your wrapper has no clue about what is happening to the wrapped object. E.g.:

// 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++;
    } 
}

A wrapper for Model just eludes invokations of makeChange() method which come from Controller callback.

like image 142
Natal Avatar answered Sep 21 '22 05:09

Natal


    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 even though we called doSomething() on the wrapper, the callback of the wrapped object got called, not the callback of the wrapper. This is what Joshua Bloch refers to when he says that "callbacks elude the wrapper".

Reference: Link

like image 40
Vipul Jain Avatar answered Sep 22 '22 05:09

Vipul Jain