• OOP中的逆变和协变


    逆变和协变在存在于强类型语言中,尽管非常少提及。可是里面蕴含了面向对象的世界观。感谢和我一起讨论这个问题的人。

    这里用了C#、Scala的语法作为演示样例。事实上逆变和协变的概念跟语言本身关系不大。事实也是如此。

    一、定义

    逆变的參数能够由指定的类型的子类型取代,协变的參数能够由指定类型的父类型取代。

    Scala中的逆变声明:Function[-A,+B] ;当中泛型-A为逆变类型,在实例化时,能够使用A类型或者A类的子类型。

    二、协变与逆变的用途不同

    1.语义

    Scala中,函数的原型之中的一个包括Function1[-A,+B]。表示一个A类型的输入,B类型的输出(返回)。替换的语法为A=>B

    这个函数的定义就好像说。我须要类A帮我做一些事情,处理完之后给你一个B。而A能够完毕的工作。其子类也应该能够完毕,这正是里氏替换原则——父类出现的地方都能够用子类取代。

    逆变强调功能——“能做什么”。

    顺便看下协变。输出为协变,表示我会给你你个B对象,假设B是肉,我当然能够说给了你食物,而食物是肉的父类,恰好是协变。假设使用逆变则说不通。

    协变强调类型——“是什么”。

    2.刀和肉的样例

    类型:食品<-肉<-牛肉。武器<-刀<-牛肉刀。(<-表示继承关系:父类<-子类)

    情景:继续拿Function1做演示样例。假设Function1须要一把刀。会生产出肉。大致为Function(A):B普通刀(刀类)会生产出普通肉(肉类),牛肉刀会生产出牛肉。

    问题:A的类型?B的类型?怎样确定

    A的类型可以为刀和牛肉刀,由于牛肉刀也是刀。

    甚至说刀的子类都可以满足条件——都有刀的功能。从继承来讲刀的子类都是刀。

    所以A的类型应该为逆变——-刀(刀和子类)

    由于做出的是肉,所以B类型肯定包括肉,但不确定是牛肉。所以我们能够设定返回为肉类型。

    对于这个情景,我们对FunctionX的终于定义为:FunctionX(-刀):肉

    没有协变?

    我们没有看到协变,实际上在C#和Scala中。我们设定一个食品类型  来接收FunctionX的返回值也不会报错。由于全部的返回类型在语言中都被声明为协变了,也就是说实际的定义是FunctionX(-刀):+肉。这么做的原因是:假设我返回了一个肉。那么这个肉一定是食品。我总能用返回类型的父类型取代返回的对象。

    这样的行为也是多态一方面的体现——在执行时改变了引用的实际类型。我觉得。这是编译层面上的协变。



    三、C#中的样例

    ICompareable<in T>强调“可比較”这一功能,是逆变。

    IEnumerable<out T>强调的是“可数的”类型。是协变。拿List<T>说明,List<肉>表示“我放了肉在列表里面”,也能够说"我放了食物在列表里面",即能够使用List<食品>取代。

    可是不能说“我放了牛肉在列表里面”,所以用List<牛肉>取代是不正确的。

  • 相关阅读:
    周记 2016.3.29
    Java ActiveMQ 讲解(一)理解JMS 和 ActiveMQ基本使用(转)
    聊聊架构01
    乐观锁和悲观所
    数据库锁(转)
    ActiveMQ消息的可靠性机制(转)
    DOM
    JavaScript
    CSS之background
    CSS之overflow
  • 原文地址:https://www.cnblogs.com/jzssuanfa/p/6767989.html
Copyright © 2020-2023  润新知