java - 标记以 super 为界的泛型类型参数
问题描述
为了说明我相当棘手的问题,需要一些解释,所以请多多包涵。
我正在为使用管理ListenerHandles
而不是removeListener(...)
可观察类中的方法的可观察模式设计简约接口。
这是这样的想法:
public interface ListenerHandle<T> {
boolean isRemoved();
void remove();
Listener<T> managedListener();
Observable<T> containingObservable();
}
public interface Observable<T> {
T get();
void set(T value);
ListenerHandle<T> addListener(Listener<T> listener);
}
public interface Listener<T> {
void onChange(ListenerHandle<T> handle, T value);
}
现在这工作得很好。
但是如果我想Observable<T>
接受更多 Listener
的generals怎么办? Listener<? super T>
准确地说。这是有道理的,因为期望的侦听器? super T
也会接受T
(它是逆变的)。
因此,ListenerHandle
需要区分它是从中获得的和T
托管的:Observable
T
Listener
public interface ListenerHandle<TL, TO> {
// ...
Listener<TL> managedListener();
Observable<TO> containingObservable();
}
public interface Observable<TO> {
// ...
<TL> ListenerHandle<TL, TO> addListener(Listener<TL> listener);
}
public interface Listener<TL> {
void onChange(ListenerHandle<TL, ? extends TL> handle, TL value);
}
即使这些接口可以编译,我们也知道TL
在
<TL> ListenerHandle<TL, TO> addListener(Listener<TL> listener);
有点太通用了,因为它现在可以是任何东西。但是,Observable
应该只能接受期望TO
或其超类型的侦听器:
<TL super TO> ListenerHandle<TL, TO> addListener(Listener<TL> listener);
这不起作用,因为super
只能用于通配符。所以另一种选择是:
ListenerHandle<? super TO, TO> addListener(Listener<? super TO> listener);
但是,在这种情况下,调用者将丢失返回ListenerHandle
的 '? super TO
和listener
'相同的信息:? super TO
Observable<Number> o = ...;
Listener<Object> l = ...;
// does not work, but should (since we know that we passed a Listener<Object>)
ListenerHandle<Object, Number> h = o.addListener(l);
// works but Object is now generalized to ? super Number
ListenerHandle<? super Number, Number> h2 = o.addListener(l);
l = h2.managedListener(); // fails because ? super Number is not (necessarily) Object
所以,我需要一种方法来指定一个带有super边界的标记类型参数,以证明参数的有界泛型类型与返回类型的有界泛型类型相同。我怎么能这样做?
解决方案
您可以将其设置为:而不是TL
为 的类型添加通用参数:Listener
Listener<? super T>
public interface ListenerHandle<T> {
boolean isRemoved();
void remove();
Listener<? super T> managedListener();
Observable<T> containingObservable();
}
public interface Observable<T> {
T get();
void set(T value);
ListenerHandle<T> addListener(Listener<? super T> listener);
}
public interface Listener<T> {
<TO extends T> void onChange(ListenerHandle<TO> handle, TO value);
// or:
// void onChange(ListenerHandle<? extends T> handle, T value);
}
如果您确实需要将TL
参数添加到ListenerHandle
,我认为唯一的方法是静态方法解决方法:
public interface ListenerHandle<TL, TO extends TL> {
boolean isRemoved();
void remove();
Listener<TL> managedListener();
Observable<TO> containingObservable();
}
public interface Observable<TO> {
TO get();
void set(TO value);
// @Deprecated
ListenerHandle<?, TO> addListener(Listener<? super TO> listener); // implies ListenerHandle<? super TO, TO>
@SuppressWarnings("unchecked")
static <TL, TO extends TL> ListenerHandle<TL, TO> addListener(Observable<TO> observer, Listener<TL> listener) {
return (ListenerHandle<TL, TO>) observer.addListener(listener);
}
}
public interface Listener<TL> {
void onChange(ListenerHandle<TL, ?> handle, TL value); // implies ListenerHandle<TL, ? extends TL>
}
您必须记住永远不要obs.addListener(lis)
直接调用,而是使用Observable.addListener(obs, lis)
. 将其标记为@Deprecated
会给您一个警告,但您还需要将其放在所有覆盖方法上。
推荐阅读
- node.js - 如何查看当前正在使用哪个 .npmrc 文件
- amazon-s3 - 在 nodejs 中使用 AWS Lambda 函数上传音频文件
- list - 如何将值附加到 Haskell 元组内的列表中?
- swift - 仅当文本字段不为空时,如何在 UIAlertController 中检查和启用操作?
- r - 使用对频率计数追踪 R 错误
- xml - XSLT 不会在节点上选择简单的移动和替换 text() 节点脚本
- c++ - c ++ lambda:Currying sum函数:使用按值捕获与按引用返回不同的结果
- javascript - Promise.all() 当所有的 Promise 都解决时永远不会触发
- php - Laravel - 尚未设置外观根
- git - git log --since 日期字符串解析