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.
Wrapper classes provide a way to use primitive data types ( int , boolean , etc..) as objects.
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.
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.
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.
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.
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
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With