【问题标题】:Type safe generic Java observer compile time error类型安全的通用 Java 观察者编译时错误
【发布时间】:2015-02-22 01:37:01
【问题描述】:

要求

我正在尝试编写 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!!!
        }
    }
}

问题

标有“编译器错误”的行与.update() 有问题:

Observer 类型中的方法 update(capture#4-of ? extends Observable) 不适用于参数 (E)

因此,即使传递给update()self 参数的类型是E extends Observable,它也不满足接口方法签名update(T observable); 其中T extends Observable。这是为什么?我真的希望它们能够兼容。

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

谢谢。

【问题讨论】:

  • 仅仅因为 E 扩展 Observable 并不意味着它会扩展任何扩展 Observable 的类(如通配符 &lt;? extends Observable&gt;
  • @hexafraction,看来编译器同意你的看法!你一定是对的,但我无法想象。你能举个例子吗? E extends Observable 为真,但它没有“扩展任何扩展 Observable 的类”的情况?
  • 这是一个反例:class A extends Observableclass B extends Observableclass A1 extends Aobs 的通配符将允许将 Observer&lt;B&gt; 分配给 obs,在这种情况下,使用 A1 实例调用 obs.update() 将是无效的。我第一次的措辞有点不对。
  • @hexafraction,很有趣。谢谢你。也许我可以找到一种方法来收紧 obs (列表内容)的类型界限。显然通配符? extends Observable 太松了。也许如果我通过自身的子类参数化 Observable 类......
  • @hexafraction,成功了!!非常感谢你的帮助。我将在下面自行回答并参考您的评论。

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


【解决方案1】:

使用 导入 io.reactivex.Observable;

而不是 导入 java.util.Observable;

【讨论】:

  • 就我而言,它是使用rx.Observable 而不是java.util.Observable
【解决方案2】:

这是为什么呢?我真的希望它们能够兼容。

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

虽然,当您尝试使用通配符 ? 时,变量 obs 的类型是 Observer&lt;? extends Observable&gt;,它可以是扩展 Observable 的任何不同的类,而不是方法参数的类型 @987654335 @。

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

是的,使用自绑定/递归泛型,具体示例是Class Enum&lt;E extends Enum&lt;E&gt;&gt;,要了解这个概念,您可以查看另见部分下面。

所以,您的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>{

}

另请参阅:

【讨论】:

  • 哇。我们在几分钟内就给出了完全相同的解决方案。 (经过研究和有用的刺激,我自己回答了。)
  • 好的,那没问题,重要的是你解决了你的问题:)
  • “自界泛型”在 Java 中几乎从不有用。 Observable 应该声明为 public abstract class Observable&lt;T&gt;Observer 声明为公共接口 Observer&lt;T&gt;。它也可以编译。
  • @newacct 不仅仅是编译,还有很多其他的解决方案可以编译好,按照你说的,即使&lt;T&gt;也不需要使用!但是,OP 不希望 Observer 中的 T 是任何类,他希望它是扩展 Observable 类的类,你有没有使用自界泛型的其他解决方案?同时,当您说它“在 Java 中几乎从来没有用处”时,我不同意您的看法。如果是这样,您能解释一下为什么在 Enum 定义中使用它们吗?
  • @newacct 这里已经有一个非常清晰和详细的答案:stackoverflow.com/questions/3061759/…
【解决方案3】:

问题

根据@hexafraction 的评论,

Observable 的观察者列表由观察者的 any 子类参数化(通配符 &lt;? extends Observer&gt;)。这不适合 Observer&lt;T extends Observable&gt;update(T observable) 方法,该方法期望 完全 Observer 参数化的 Observable 类型。

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

解决方案

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

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参数化:

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

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

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

类主体和行为不受影响,但如果 Observers 和 Observables 相互绑定,编译器会报错。

用法

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

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

单独参数化一个 Observable(模型)并调用 updateObservers():

class Model extends Observable<Model> {
    public void doStuff() {
        //...
        updateObservers(this);
    }
}

【讨论】:

  • 边界是不必要的。 Observable 应该声明为 public abstract class Observable&lt;T&gt;Observer 声明为公共接口 Observer&lt;T&gt;。它也可以编译。
  • 刚刚测试过——你是对的!我不喜欢不必要的限制,所以我会引用你并修正我的答案。
【解决方案4】:

如下改变观察者界面:

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

【讨论】:

  • 实际上问题在于 Observable 的观察者列表过于宽松,无法在 update() 方法中使用。无论如何,谢谢。
猜你喜欢
  • 2017-07-02
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2014-07-25
  • 1970-01-01
  • 2012-08-26
相关资源
最近更新 更多