分享一个关于协变、逆变、不变的优秀回答 - V2EX
V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
请不要在回答技术问题时复制粘贴 AI 生成的内容
JasonLaw
V2EX    程序员

分享一个关于协变、逆变、不变的优秀回答

  •  
  •   JasonLaw 2020-11-12 12:00:29 +08:00 2373 次点击
    这是一个创建于 1845 天前的主题,其中的信息可能已经有所发展或是发生改变。

    之前对于这几个概念没有一个深入的理解,这个回答真的解释得太好了。

    关于数组是协变的,Jon Skeet 在Is List<Dog> a subclass of List<Animal>? Why are Java generics not implicitly polymorphic? - Stack Overflow中有这么一个评论

    6 条回复    2020-11-16 02:21:24 +08:00
    sunjourney
        1
    sunjourney  
       2020-11-12 13:25:03 +08:00
    这不算解释吧,原因在于数据是否有可变性
    no1xsyzy
        2
    no1xsyzy  
       2020-11-12 13:51:40 +08:00
    只有将类型作为参量(包括泛型)才有协变逆变之说
    假定一个函数签名为 f: A -> B
    A 对于 f 是逆变的,因为对于任何 A 的父类 C,g: C -> B 可以和 f 一样被使用
    B 对于 f 是协变的,因为对于任何 B 的子类 D,h: A -> D 可以和 f 一样被使用
    而如果是 List 这个具有泛型的类,因为它的 “范畴”(记得面向对象是范畴论的近似)既包含了列表取元素的 ref: A[] -> A,又包含了元素构造列表的 list: A -> A[],甚至包含了尾部添加元素的 append: A[] -> A -> A[] 和头部添加元素的 prepend: A -> A[] -> A[],那 A 对于 A[] 既不总是协变的,也不总是逆变的,那就是不变的。

    而即使底层实现了类似的结构,如果是只出不进的队列,那 A 对于 QueueOut 是协变的,只进不出的队列,A 对于 QueueIn 是逆变的。
    (这似乎和 traits 有点类似?接口的某些子集达成协变,某些子集达成逆变,合在一起就成了不变)

    整套 OO 体系是混乱的。
    滥用继承。
    希望来点只能进行包裹、静态鸭子语言的 OO 语言。
    顺便这需要语法糖来快速实现 this.some_method = this.wrapped_object.method
    `convey wrapped_object.method as some_method`
    JasonLaw
        3
    JasonLaw  
    OP
       2020-11-12 16:29:55 +08:00
    @sunjourney #1 数据是否有可变性?
    JasonLaw
        4
    JasonLaw  
    OP
       2020-11-12 16:31:07 +08:00
    @no1xsyzy #2 完全看不懂你所说的,可能是我很多不懂吧。
    aguesuka
        5
    aguesuka  
       2020-11-12 17:32:48 +08:00 via Android
    既然选择了 java 就不要纠结这种东西,java 不是依赖类型的语言。泛型边界是对代码可读性的毁灭打击
    xiuy
        6
    xiuy  
       2020-11-16 02:21:24 +08:00
    这个回答不能称得上是对协变、逆变的解释,只是针对这个问题来说,这个答案还是讲得挺明白的。

    要理解 covariant 和 contravariant 的话,首先要明白的是 subtype 的基础概念。

    > Type S is a subtype of a type T, written S <: T, if an expression of type S can be used in any context that expects an element of type T.

    举个例子的话,就是任何需要一个 Animal 的地方,都可以放进去一个 Dog,那么 Dog <: Animal 。(看着很像 Inheritance 是因为大多数的程序语言把 Inheritance 和 Subtyping 混在一起了)

    而协变与逆变其实与 Function Subtyping 有关。(也就是 #2 中的「只有将类型作为参量(包括泛型)才有协变逆变之说」)

    Function Subtyping 的定义是这样婶儿滴:(S' -> T') <: (S -> T) if S <: S' and T' <: T.

    协变和逆变就是从这里来的,这里的 S <: S' 就是逆变,而 T' <: T 是协变。

    说直白一点就是 T' <: T 是顺着 (S' -> T') <: (S -> T) 来的,而 S <: S' 这个关系要求逆过来。

    再叨叨多一点,这个定义其实挺反直觉的,如果要 (S' -> T') <: (S -> T) 的话,我们可能以为条件应该是 S' <: S 和 T' <: T 。这时候需要回头在看一下 subtypes 的定义:*S <: T, if an expression of type S can be used in any context that expects an element of type T*. **当 T 变成了 f, 问题就转为寻找一个能代替 f 的 f'。**

    这个图示非常有助于理解:

    ![Untitled.png]( https://i.loli.net/2020/11/16/Hy3fKGed5gYI6uv.png)

    S 需要是 S' 的 subtype ( S <: S'),任何原来的输入才能放得进去;而 T' 必须是 T 的 subtype,任何出来的 T' 才能被视作 T 。

    对于有范型的类来说,我觉得 [Scala 这个文档]( https://docs.scala-lang.org/zh-cn/tour/variances.html) 讲得挺好的,不会 Scala 也能看懂。
    关于     帮助文档     自助推广系统     博客     API     FAQ     Solana     5268 人在线   最高记录 6679       Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 26ms UTC 08:41 PVG 16:41 LAX 00:41 JFK 03:41
    Do have faith in what you're doing.
    ubao msn snddm index pchome yahoo rakuten mypaper meadowduck bidyahoo youbao zxmzxm asda bnvcg cvbfg dfscv mmhjk xxddc yybgb zznbn ccubao uaitu acv GXCV ET GDG YH FG BCVB FJFH CBRE CBC GDG ET54 WRWR RWER WREW WRWER RWER SDG EW SF DSFSF fbbs ubao fhd dfg ewr dg df ewwr ewwr et ruyut utut dfg fgd gdfgt etg dfgt dfgd ert4 gd fgg wr 235 wer3 we vsdf sdf gdf ert xcv sdf rwer hfd dfg cvb rwf afb dfh jgh bmn lgh rty gfds cxv xcv xcs vdas fdf fgd cv sdf tert sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf shasha9178 shasha9178 shasha9178 shasha9178 shasha9178 liflif2 liflif2 liflif2 liflif2 liflif2 liblib3 liblib3 liblib3 liblib3 liblib3 zhazha444 zhazha444 zhazha444 zhazha444 zhazha444 dende5 dende denden denden2 denden21 fenfen9 fenf619 fen619 fenfe9 fe619 sdf sdf sdf sdf sdf zhazh90 zhazh0 zhaa50 zha90 zh590 zho zhoz zhozh zhozho zhozho2 lislis lls95 lili95 lils5 liss9 sdf0ty987 sdft876 sdft9876 sdf09876 sd0t9876 sdf0ty98 sdf0976 sdf0ty986 sdf0ty96 sdf0t76 sdf0876 df0ty98 sf0t876 sd0ty76 sdy76 sdf76 sdf0t76 sdf0ty9 sdf0ty98 sdf0ty987 sdf0ty98 sdf6676 sdf876 sd876 sd876 sdf6 sdf6 sdf9876 sdf0t sdf06 sdf0ty9776 sdf0ty9776 sdf0ty76 sdf8876 sdf0t sd6 sdf06 s688876 sd688 sdf86