我想了解逆转,它是如何运作的。请考虑以下句子:
这句话让我感到困惑。在第一部分令人困惑的是,逆变意味着F [B]类型是一个子类型 如果A是B
的子类型,则为[A]
F[B] is subtype of F[A]
突然出现第二部分A is a subtype of B
的原因?它自相矛盾?
协方差更清晰:
协方差意味着类型F [B]是类型F [A]的子类型,如果B 是A的子类型。
第一部分是 F [B]是子类型,第二部分 B是子类型。
答案 0 :(得分:3)
在此问题的评论中使用Json示例:
trait Shape {
val area: Double
}
case class Circle(radius: Double) extends Shape {
override val area = math.Pi * radius * radius
}
def writeJson(circles: List[Circle], jsonWritingFunction: Circle => String): String =
circles.map(jsonWritingFunction).mkString("\n")
def circleWriter(circle: Circle): String =
s"""{ "type" : "circle writer", radius : "${circle.radius}", "area" : "${circle.area}" }"""
def shapeWriter(shape: Shape): String =
s"""{ "type" : "shape writer", "area" : "${shape.area}" }"""
然后这两个都是可以接受的:
writeJson(List(Circle(1), Circle(2)), circleWriter)
writeJson(List(Circle(1), Circle(2)), shapeWriter)
结果
// first writeJson
{ "type" : "circle writer", "radius" : "1.0", "area" : "3.141592653589793" }
{ "type" : "circle writer", "radius" : "2.0", "area" : "12.566370614359172" }
// first writeJson
{ "type" : "shape writer", "area" : "3.141592653589793" }
{ "type" : "shape writer", "area" : "12.566370614359172" }
即使jsonWritingFunction
期望Circle => String
,我们也可以通过Shape => String
声明传递Function1
:trait Function1[-T1, +R]
。第一种类型(T1
)是逆变的。
因此Shape => String
是Circle => String
的子类型,因为Circle
是Shape
的子类型。
答案 1 :(得分:1)
我有一种直觉,我觉得有助于理解协方差和逆变。请注意,这不是一个严格的定义。直觉如下:
A
的值,这与该类的用户只能从类中读取类型A
的值相同,则它是协变的类型A
A
的值作为输入,这与说该类的用户只能将类型A
的值写入类的相同,它是类型A
举一个简单的例子,考虑两个接口Producer[A]
和Consumer[A]
:
trait Producer[A] {
def produce():A
}
trait Consumer[A] {
def consume(value:A):Unit
}
一个只输出A
类型的值(因此您从A
“读取”Producer[A]
)而另一个接受它们作为参数(因此您“写”{{1}到A
)。
现在考虑方法Producer[A]
:
connect
如果您想一下这个def connect[A](producer:Producer[A], consumer:Consumer[A]): Unit = {
val value = producer.produce()
consumer.consume(value)
}
不是以最通用的方式编写的。考虑类型层次结构connect
&lt ;: Parent
<:Main
。
Child
,它可以处理Consumer[Main]
和Main
,因为任何Child
实际上都是Child
。因此,Main
可以安全地连接到Consumer[Main]
和Producer[Main]
。Producer[Child]
。它产生Producer[Main]
。哪个Main
可以处理?显然Consumer
和Consumer[Main]
因为每个Consumer[Base]
都是Main
。但是,Base
无法安全处理,因为并非每个Consumer[Child]
都是Main
因此,创建最通用的Child
的一个解决方案就是这样写:
connect
换句话说,我们明确说明有两种不同的泛型def connect[A <: B, B](producer:Producer[A], consumer:Consumer[B]): Unit = {
val value = producer.produce()
consumer.consume(value)
}
和A
,另一种是另一种的父类。
另一种解决方案是修改B
和Producer
类型,使Consumer
类型的参数接受在此上下文中安全的任何Producer[A]
类似地,类型Producer
的参数将接受在此上下文中安全的任何Consumer[A]
。正如您可能已经注意到Consumer
的规则是“协变”但“消费者”的规则是“逆变”(因为记住您希望Producer
成为{{1}的安全子类型})。所以替代解决方案是写:
Consumer[Base]
此解决方案更好,因为它通过一次更改涵盖了所有情况。显然,在Consmer[Main]
使用trait Producer[+A] {
def produce():A
}
trait Consumer[-A] {
def consume(value:A):Unit
}
def connect[A](producer:Producer[A], consumer:Consumer[A]): Unit = {
val value = producer.produce()
consumer.consume(value)
}
安全的任何情况下,Consumer[Main]
也是安全的。