本文介绍了对GenericFutureListener中使用的Netty泛型类型感到困惑吗?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

在netty中,定义侦听器,如下所示:例如在io.netty.util.concurrent.CompleteFuture类中:

@Override
public Future<V> addListener(GenericFutureListener<? extends Future<? super V>> listener) {
    if (listener == null) {
        throw new NullPointerException("listener");
    }
    DefaultPromise.notifyListener(executor(), this, listener);
    return this;
}

据我所知:真的吗? super V>表示继承V的Class的集合.因此,它是继承树中的集合,因为至少我们有Future< V>和Future< Object>.假设集合的名称为C.

那么问题来了,那是什么?扩展C意味着C是集合吗?

希望有人能启发我!

解决方案

我假设您的上述声明来自Netty的 Promise 类.我认为无论如何我的回答都应该有效,因为您的问题似乎与协方差和协方差有关,而不是与Netty的API有关.

public interface Promise<V> extends Future<V> {

   Promise<V> addListener(GenericFutureListener<? extends Future<? super V>> listener);
   //...
}

此处泛型的目的是使API对其用户更具延展性.

假设您有三个不同的GenericFutureListener对象,它们具有三种不同类型的参数:IntegerNumberObject.

GenericFutureListener<Future<Integer>> fl1 = System.out::println;
GenericFutureListener<Future<Number>> fl2 = System.out::println;
GenericFutureListener<Future<Object>> fl3 = System.out::println;

请注意,IntegerNumber的子类型,而子类型又是Object的子类型.

现在假设我们有一个类型为IntegerPromise,如下所示

Promise<Integer> p = somePromise;

我们的方法声明将被编译器解释为

Promise<Integer> addListener(GenericFutureListener<? extendsFuture<? super Integer>> listener);

基本上是说GenericFutureListener可能在Integer或其任何超级类型的期货上运行.

这显然使API更加灵活,例如,我可以添加我之前定义的任何侦听器,以便在我对Integer的承诺得到解决时得到通知:

p.addListener(fl1);
p.addListener(fl2);
p.addListener(fl3);

请注意,我没有被迫为明确类型为Integer的未来提供侦听器.如果您认为这完全有意义,因为如果我的诺言p产生了Integer,而Integer Number,那么一个知道如何处理未来的侦听器Number的值也应该能够处理Integer的将来.如果我的监听器知道如何处理Object的将来,并且Integer Object,那么让监听器实现<Object处理Integer的未来,对吧?

好吧,这正是Future<? super V>在上面的声明中的含义.这就是所谓的逆方差.

现在,事实是,在Netty中,Future是一个接口,许多不同的类都可以实现Future.我们希望GenericFutureListener能够使用Future的任何子类型,而不仅仅是Future本身,对吧?

例如,Promise实际上是Future的子类型:

GenericFutureListener<Promise<Integer>> fl4 = System.out::println;
GenericFutureListener<Promise<Number>> fl5 = System.out::println;
GenericFutureListener<Promise<Object>> fl6 = System.out::println;

如您所见,

@Override
public Future<V> addListener(GenericFutureListener<? extends Future<? super V>> listener) {
    if (listener == null) {
        throw new NullPointerException("listener");
    }
    DefaultPromise.notifyListener(executor(), this, listener);
    return this;
}

在这里接受Promise作为类型参数.这要归功于<? extends Future>的声明.没有它,GenericFutureListener在这里只能接受Future类型,这将使​​该API的灵活性大大降低,对吧?

此概念称为协方差,它再次用于使API对其用户更加灵活.

现在,我们可以做出最初的承诺,同时添加第二组侦听器:

p.addListener(fl4);
p.addListener(fl5);
p.addListener(fl6);

就在那里.得益于协方差和逆方差的使用,API更加灵活.

In netty define the listener as below:for example in the class io.netty.util.concurrent.CompleteFuture:

@Override
public Future<V> addListener(GenericFutureListener<? extends Future<? super V>> listener) {
    if (listener == null) {
        throw new NullPointerException("listener");
    }
    DefaultPromise.notifyListener(executor(), this, listener);
    return this;
}

From what I've known:Futrue<? super V> means a collections of Class which supers V. So it is a collections as in the inheritance tree, for we have Future<V> and Future<Object> at least. Let's say the collections' name is C.

So here comes the question, what does ? extends C means while C is a collection?

Hope somebody could enlighten me!

解决方案

I'm assuming your declaration above comes from Netty's Promise class. I think my answer should work irrespective of that because your question seems to be about covariance and contravariance more than about Netty's API explicitly.

public interface Promise<V> extends Future<V> {

   Promise<V> addListener(GenericFutureListener<? extends Future<? super V>> listener);
   //...
}

The intention of generics here is to make the API more malleable for its users.

Suppose you have three different GenericFutureListener objects of three different type arguments: Integer, Number and Object.

GenericFutureListener<Future<Integer>> fl1 = System.out::println;
GenericFutureListener<Future<Number>> fl2 = System.out::println;
GenericFutureListener<Future<Object>> fl3 = System.out::println;

Notice that Integer is a subtype of Number which in turn is a subtype of Object.

Suppose now that we have a Promise of type Integer, somewhat as follows

Promise<Integer> p = somePromise;

Our method declaration would be interpreted by the compiler as

Promise<Integer> addListener(GenericFutureListener<? extendsFuture<? super Integer>> listener);

Which basically is saying that the GenericFutureListener might operate on futures of type Integer, or any of its super types.

This clearly makes the API much more flexible, for example, I can add any of the listeners I defined before to be notified when my promise of Integer is resolved:

p.addListener(fl1);
p.addListener(fl2);
p.addListener(fl3);

Notice that I was not forced to provide a listener for a future of explicitly type Integer. If you think about it that totally makes sense because if my promise p produces an Integer, and an Integer is a Number, then a listener that knows how to handle a future of Number should be able to handle a future of Integer as well. And if I have listener that knows how to handle a future of Object, and an Integer is an Object, then there should be no problem in letting a listener for a future of Object to handle a future of Integer, right?

Well, that's exactly what Future<? super V> means in the declaration above. This is concept known as contravariance.

Now, the truth is that in Netty Future is an interface, and many different classes may implement Future. We would like our GenericFutureListener to be able to use any subtype of Future and not just Future itself, right?.

For example, a Promise is in fact a subtype of Future:

GenericFutureListener<Promise<Integer>> fl4 = System.out::println;
GenericFutureListener<Promise<Number>> fl5 = System.out::println;
GenericFutureListener<Promise<Object>> fl6 = System.out::println;

And as you can see, GenericFutureListener accepts Promise as a type argument here. This is so thanks to the declaration of <? extends Future>. Without it, GenericFutureListener would only accept the Future type here and that would make this API much less flexible, right?.

This concept is called covariance and once again it is used to make the API much more flexible for its users.

Now we can make our original promise also add these second set of listeners:

p.addListener(fl4);
p.addListener(fl5);
p.addListener(fl6);

And there you have it. A much more flexible API thanks to the proper use of covariance and contravariance.

这篇关于对GenericFutureListener中使用的Netty泛型类型感到困惑吗?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!

10-14 15:59