【问题标题】:Label a generic type argument bounded with super标记以 super 为界的泛型类型参数
【发布时间】:2019-04-12 02:11:42
【问题描述】:

为了说明我比较棘手的问题,需要一些解释,所以请多多包涵。

我正在为 observable 模式设计极简界面,该模式使用管理 ListenerHandles 而不是 observable 类中的 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&lt;T&gt;接受更笼统的 Listeners? Listener&lt;? super T&gt;s 是准确的。这是有道理的,因为期望? super T 的侦听器也将接受T(它是逆变的)。

因此,ListenerHandle 需要区分 T 的来源 Observable 和托管 TListener

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 TOlistener? 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 为界的 labeled 类型参数,以证明该参数的有界泛型类型与返回类型的有界泛型类型。我怎么能这样做?

【问题讨论】:

    标签: java generics types


    【解决方案1】:

    您可以将其设为Listener&lt;? super T&gt;,而不是为Listener 类型添加通用参数TL

    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,我认为唯一的方法是static method workaround

    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 会给您一个警告,但您还需要将其放在所有覆盖方法上。

    【讨论】:

    • 这确实有效,但(据我所知)不幸的是,它并不比我最后的方法好。在返回的ListenerHandle 中,我仍然丢失了我传递给Observable实际 Listener 类型的信息,并且必须推广到Listener&lt;? super Number&gt; 而不是Listener&lt;Object&gt;。跨度>
    • @Reizo 真的有必要知道确切的类型吗?似乎重要的部分是它保证接受来自ListenerHandleObservable 的泛型类型的值。
    • 这在很大程度上取决于用例。我想编写一个通用集合库,并且由于信息(关于类型)在概念上是存在且清晰的,我想有一种方法来捕获它。但是我倾向于认为这根本不可能......但是当然,你反对它(很可能)没有必要是非常合理的。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-08-15
    • 1970-01-01
    相关资源
    最近更新 更多