之前对于这几个概念没有一个深入的理解,这个回答真的解释得太好了。
关于数组是协变的,Jon Skeet 在Is List<Dog> a subclass of List<Animal>? Why are Java generics not implicitly polymorphic? - Stack Overflow中有这么一个评论。
1
sunjourney 2020-11-12 13:25:03 +08:00
这不算解释吧,原因在于数据是否有可变性
|
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` |
3
JasonLaw OP @sunjourney #1 数据是否有可变性?
|
5
aguesuka 2020-11-12 17:32:48 +08:00 via Android
既然选择了 java 就不要纠结这种东西,java 不是依赖类型的语言。泛型边界是对代码可读性的毁灭打击
|
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 也能看懂。 |