java中协变跟逆变是对泛型类的继承关系的表述.
我们提供的服务有:网站制作、成都做网站、微信公众号开发、网站优化、网站认证、恭城ssl等。为上1000+企事业单位解决了网站和推广的问题。提供周到的售前咨询和贴心的售后服务,是有科学管理、有技术的恭城网站制作公司
如:
ListNumber 和 ListInteger 之间是没有继承关系的.
但是直观上会觉得, Integer 是 Number 的子类, 所以 ListInteger 应是 ListNumber 的子类.
如果想要这种效果, 就要用协变.
List? extends Number 这样 ListInteger 就能成为List? extends Number 子类, 也就是可以赋值
这里如果你想要相反的效果, 则用逆变, List? super Number 这样继承关系就会相反.
那么什么时候用协变,逆变?
协变主要是用在函数的返回值上,逆变用在函数参数上,这样的规则也就能遵循 里氏替换原则 .
如 Function , 在这里 R 作为函数的返回值, 所以这个泛型要协变, 而 T 用在函数的参数上所以要用逆变
这里举个例子
假设有以下继承关系:
车 轿车 标准轿车 高级轿车
现在有一个人声称自己能修理所有的标准轿车, 所以发出了以下公告:
假设我现在有 List轿车 和 List高级轿车
那么这个人到底能修理哪个呢? 从上面的函数声明来看都不可以.
再来看看这个人的声明
他说能够修理所有 标准轿车
那么因为标准轿车扩展了轿车, 所以如果能够修理标准轿车, 那么应当可以修理轿车
所以这个函数应当可以接受所有 标准轿车 的父类
也就是说 List轿车 能够传入 以 List标准轿车 为参数的函数
换句话说 List轿车 是 List标准轿车 的子类, 这样才能传入参数
所以上面的公告要用逆变, 改成如下:
也许也不会有人想把自己的高级轿车交给这家伙.
以此类推, 函数的返回值应当用协变, 这样既能满足 里氏替换原则 了
关于协变和逆变要从面向对象继承说起。继承关系是指子类和父类之间的关系;子类从父类继承所以子类的实例也就是父类的实例。比如说Animal是父类,Dog是从Animal继承的子类;如果一个对象的类型是Dog,那么他必然是Animal。
协变逆变正是利用继承关系 对不同参数类型或返回值类型 的委托或者泛型接口之间做转变。我承认这句话很绕,如果你也觉得绕不妨往下看看。
如果一个方法要接受Dog参数,那么另一个接受Animal参数的方法肯定也可以接受这个方法的参数,这是Animal向Dog方向的转变是逆变。如果一个方法要求的返回值是Animal,那么返回Dog的方法肯定是可以满足其返回值要求的,这是Dog向Animal方向的转变是协变。
当前。NET语言如VB和C#还不支持泛型的协变(covariance)与逆变(contravariance)。尽管微软中的很多人也在谈论它,但是在不远的将来这还是不太可能出现。对协变与逆变的完整介绍要花很长时间。基于此,请大家参考Eric Lippert的关于C#中的协变与逆变的系列文章。为了在VB中增加协变与逆变的泛型支持,Lucian Wischik提出了下面的语法。
类型参数可由关键字“In”和“Out”修饰。“In”类型只能作为方法参数。与此类似,“Out”类型只能作为方法的返回类型。
使用Out类型的一个例子就是IEnumerable(Of T)。如果某函数接受一个IEnumerable(Of Animal)类型参数,那么我们就可以给它传一个IEnumerable(of Bird)。对于In类型,一个不太恰当的例子就是顺序。看一下下面的接口:Interface IWriter(Of T) Write(value As T)
如果你向接受Writer(Of Animal)类型参数的函数传一个IWriter(Of Bird),当然就不对了。该方法可以将Animal的任何子类传给IWriter.Write,但是它只接受Birds.如果使用注解,该接口看起来像下面这样:Interface IEnumerable(Of Out T)Interface IWriter(Of In T)
这是针对VB编写的,它也可以用在C#上。
interface IEnumerableinterface IWriter不幸的是,这种语法并不能直接应用在大多数常见的场景中。比如IList(Of T),当传给一个向集合中写入的方法时,T应该是In类型。但是当传给一个从集合中读取的方法时,T应该是Out类型。或许这里应该针对IList创建一个基类,该类会将接受T与返回T的方法区分开来。
追溯过去,C#和VB都支持数组协变(out/IEnumerable情况),尽管在逆变的情况下这会导致运行时错误(in/IWriter情况)。这样做的目的是使C#更兼容于Java.大多数人都认为这是一个不好的设计,但是现在却无法改变了。
当前 NET语言如VB和C#还不支持泛型的协变(covariance)与逆变(contravariance) 尽管微软中的很多人也在谈论它 但是在不远的将来这还是不太可能出现 对协变与逆变的完整介绍要花很长时间 基于此 请大家参考Eric Lippert的关于C#中的协变与逆变的系列文章 为了在VB中增加协变与逆变的泛型支持 Lucian Wischik提出了下面的语法 类型参数可由关键字 In 和 Out 修饰 In 类型只能作为方法参数 与此类似 Out 类型只能作为方法的返回类型 使用Out类型的一个例子就是IEnumerable(Of T) 如果某函数接受一个IEnumerable(Of Animal)类型参数 那么我们就可以给它传一个IEnumerable(of Bird) 对于In类型 一个不太恰当的例子就是顺序 看一下下面的接口 Interface IWriter(Of T) Write(value As T) 如果你向接受Writer(Of Animal)类型参数的函数传一个IWriter(Of Bird) 当然就不对了 该方法可以将Animal的任何子类传给IWriter Write 但是它只接受Birds 如果使用注解 该接口看起来像下面这样 Interface IEnumerable(Of Out T)Interface IWriter(Of In T) 这是针对VB编写的 它也可以用在C#上
lishixinzhi/Article/program/net/201311/14605
用来表明传入传出参数的,比如IEnumerable的Current的T就是一个out,这样IEnumerablestring就可以赋值给IEnumerableobject,因为Current是一个取值的属性(输出 out)所以使用object引用子类string是没问题的。
而in表明输入参数,比如IComparer的Compare接受两个T的参数标明为in,这样ICompareobject可以传给IComparestring,因为string是object的子类,所以ICompareobject的Convert方法的object参数可以引用被当作IComparestring而传入的string类型的参数,string是object的子类。
如果你不理解那就不用关心了,因为这种协变逆变的高级功能只有等你需要的时候才能理解。