键入安全通用Java观察器编译时错误

时间:2015-02-22 01:37:02

标签: java generics design-patterns observer-pattern type-safety

要求

我正在尝试编写Observer / Observable对类。我想参数化Observer,以便可以进行类型安全更新调用。想象一下这个版本:

class View implements Observer<Model> {
    @Override
    public void update(Model model) { render(model); }  // no casting:)
}

而不是需要强制转换的版本:

class View implements Observer {
    @Override
    public void update(Object model) { render((Model) model); }  // casting:(
}

尝试

这是我到目前为止所拥有的。我的Observer界面:

public interface Observer<T extends Observable> {
    public void update(T observable);
}

和我的Observable抽象类:

import java.util.List;

public abstract class Observable {
    private List<Observer<? extends Observable>> observers;

    public Observable() {
        System.out.println(this.getClass());
    }

    public void addObserver(Observer<? extends Observable> obs) {
        observers.add(obs);
    }

    public void removeObserver(Observer<? extends Observable> obs) {
        observers.remove(obs);
    }

    protected <E extends Observable> void updateObservers(E self) {
        for (Observer<? extends Observable> obs : observers) {
            obs.update(self);  // <-------------------- COMPILER ERROR!!!
        }
    }
}

问题

标有&#34; COMPILER ERROR&#34; .update()

有问题
  

Observer类型中的方法更新(捕获#4-of?extends Observable)不适用于参数(E)

因此,即使传递给self的{​​{1}}参数的类型为update(),它也不满足E extends Observable所在的接口方法签名update(T observable);。这是为什么?我真的希望那些兼容。

我可以解决这个问题以满足我的要求吗?

感谢。

4 个答案:

答案 0 :(得分:9)

使用 import io.reactivex.Observable;

而不是 import java.util.Observable;

答案 1 :(得分:4)

  

为什么?我真的希望那些兼容。

因为函数public void update(T observable);T类具有相同的泛型参数Observer<T extends Observable>,这意味着它的参数类型应该与引用的类型相同将调用update()函数的变量。

然而,当您尝试使用通配符?时,变量obs的类型为Observer<? extends Observable>,它可以是扩展Observable的任何不同类型方法参数是E

  

我可以解决这个问题以满足我的要求吗?

是的,使用 Self Bound / Recursive Generics ,一个具体的例子就是Class Enum<E extends Enum<E>>,要理解这个概念你可以看一下参见以下部分。

因此,您的Observable将类似于:

import java.util.List;

public abstract class Observable<T extends Observable<T>> {
    private List<Observer<T>> observers;

    public Observable() {
        System.out.println(this.getClass());
    }

    public void addObserver(Observer<T> obs) {
        observers.add(obs);
    }

    public void removeObserver(Observer<T> obs) {
        observers.remove(obs);
    }

    protected  void updateObservers(T self) {
        for (Observer<T> obs : observers) {
            obs.update(self);  
        }
    }
}

Observer界面:

public interface Observer<T extends Observable<T>> {
    public void update(T observable);
}

View类:

class View implements Observer<Model> {
    @Override
    public void update(Model model) { render(model); } 
}

假设您的Model类应该是这样的:

public class Model extends Observable<Model>{

}

另见:

答案 2 :(得分:2)

问题

根据@ hexafraction的评论,

Observable的观察者列表由Observer的任何子类(通配符<? extends Observer>)参数化。这与Observer<T extends Observable>的{​​{1}}方法不太匹配,该方法期望完全观察者参数化的Observable类型。

  

这是一个反例:A类扩展Observable,B类扩展Observable,A1类扩展A. obs的通配符允许Obse​​rver分配给obs,在这种情况下调用obs.update()实例A1无效。

解决方案

解决方案也是对Observable类进行参数化,以便对Observer update(T observable)方法正确约束List内容:

update()

观察者通过自己的public abstract class Observable<E> { private List<Observer<E>> observers; public Observable() { observers = new ArrayList<>(); } public void addObserver(Observer<E> obs) { observers.add(obs); } public void removeObserver(Observer<E> obs) { observers.remove(obs); } protected void updateObservers(E self) { for (Observer<E> obs : observers) { obs.update(self); } } }

进行参数化
T

如果您需要类型约束,以便Observers 观察Observables,和/或Observables可能将Observables发送到Observer更新,则使用这些类签名:

public interface Observer<T> {
    public void update(T observable);
}

类主体和行为不受影响,但如果Observers和Observables彼此绑定,编译器会抱怨。

用法

通过Observable(模型)参数化观察者(视图):

public interface Observer<T extends Observable<T>> { ... }
public abstract class Observable<E extends Observable<E>> { ... }

自行参数化Observable(模型)并调用class View implements Observer<Model> { @Override public void update(Model model) { render(model); } // no casting:) }

updateObservers()

答案 3 :(得分:0)

更改Observer界面,如下所示:

public interface Observer<T extends Observable> {
    public <E extends Observable> void update(E observable);
}