包装类的缺点很少。需要注意的是,包装类不适合在回调框架中使用,其中对象将自引用传递给其他对象以进行后续调用(“回调”)。因为包装对象不知道它的包装器,它会传递对它自己的引用(this),并且回调会逃避包装器。
有人可以用一个例子来解释这意味着什么。它是用Effective Java编写的,但我并没有完全理解它。
要添加到上下文,而不是继承,我们应该支持组合而不是对Set
进行子类化,我们应该使用这样的东西:
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); }
...
}
但是,这将如何失败,我仍然无法理解回调。在JavaScript中,我们可以使用函数回调,但如果有人可以解释,相同的概念如何应用于Java。
答案 0 :(得分:8)
如果你能保证你总是在任何地方(对于未来的回调)传递被转发的对象的引用,那么一切都可以。尽管如此,你可以创建一个对象,用一些类包装它,但是该对象本身可以有一些方法在某处传递 this ,例如某些侦听器或其他地方。在这种情况下,您的包装器不知道包装对象发生了什么。 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++;
}
}
Model
的包装器只能避免来自makeChange()
回调的Controller
方法的调用。
答案 1 :(得分:2)
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();
}
问题在于,即使我们在包装器上调用了doSomething(),包装对象的回调也被调用了,而不是包装器的回调。这就是约书亚·布洛赫(Joshua Bloch)所说的“回调不包含包装材料”。
参考:Link