我正在努力理解contravariance,是如何工作的。考虑以下几句:
令人费解的是,如果A是B的一个亚型,则FB是FA的一个亚型。
这句话让我很困惑。在第一部分,F[B] is subtype of F[A],突然为什么第二部分是A is a subtype of B?它自相矛盾吗?
协方差比较清楚:
协方差表示,如果B是A的子类型,则FB是FA类型的子类型。
第一部分是,FB是的子类型,第二个是B是的子类型。
发布于 2018-01-31 15:14:03
在这个问题的注释中使用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,但由于Function1的声明:trait Function1[-T1, +R],我们可以传递一个Shape => String。第一个类型(T1)是相反的。
因此,Shape => String是Circle => String的一个子类型,因为Circle是Shape的一个子类型。
发布于 2018-02-01 00:49:19
我有一种直觉,我发现这有助于理解协方差和反方差。但请注意,这并不是一个严格的定义。直觉如下:
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类型的值(因此您从Producer[A]“读取”A ),而另一个则接受它们作为参数(因此您将A“写”到Producer[A])。
现在考虑一下方法connect
def connect[A](producer:Producer[A], consumer:Consumer[A]): Unit = {
val value = producer.produce()
consumer.consume(value)
}如果您想一下,这个connect不是以最通用的方式编写的。考虑类型层次结构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的规则是“协变”的,但是“消费者”的规则是“反变体”(请记住,您希望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]安全使用的任何上下文中,Consumer[Base]也是安全的。
https://stackoverflow.com/questions/48544153
复制相似问题