pin*_*ons 1 generics types scala contravariance
给出以下代码
sealed trait Fruit
case class Apple(color: String) extends Fruit
case class Orange(color: String) extends Fruit
def getAppleColor(apple: Apple) = apple.color
def getOrangeColor(orange: Orange) = orange.color
val myMap: Map[String, Fruit] = Map(
"myApple" -> Apple("red"),
"myOrange" -> Orange("orange"),
)
val myMapOfFunctions: Map[String, Apple with Orange => String] = Map(
"myAppleColorFun" -> getAppleColor,
"myOrangeColorFun" -> getOrangeColor,
)
Run Code Online (Sandbox Code Playgroud)
为什么myMapOfFunctions不是 aMap[String, Fruit => String]类似于myMap?我想是因为它与功能有关,但我想更好地理解原因。谢谢!
我只是想理解为什么编译器说地图的类型是 Apple with Orange 而不是 Fruit
好的,好处是这很“容易”解释。
不好的是它可能不那么容易理解。
让我们退后几步,使用 anif而不是集合来稍微简化代码。
当你做这样的事情时:
val foo = if (bar) x else y
Run Code Online (Sandbox Code Playgroud)
编译器必须推断 的类型foo,为此,它将首先获取/推断x和的类型y;让我们分别调用这些X& Y,然后计算两者之间的 LUB (最小上限),从而产生一个新类型Z,该类型将成为分配给的类型。这是foo
有道理的X <: Z,因为Y <: ZLiskov 受到尊重。
快速注意,如果
X和Y是相同类型A,那么 LUB 只是A
另一个快速类型,如果X是 的子类型,Y那么 LUB 只是Y
让我们看看那些应用于简单类型的:
val fruit = if (true) Apple(color = "red") else Orange(color = "green")
Run Code Online (Sandbox Code Playgroud)
这里,一个分支的类型Apple为 ,另一个分支的类型Orange为 ,两者之间的 LUB 为Fruit。
到目前为止,一切都很简单。
现在,让我们把事情变得有趣一点:
val optApple: Option[Apple] = Apple(color = "red")
val optOrange: Option[Orange] = Orange(color = "green")
val optFruit = if (true) optApple else optOrange
Run Code Online (Sandbox Code Playgroud)
这里一个分支是Option[Apple],另一个分支是Option[Orange],我们知道结果是Option[Fruit],但是为什么呢?
好吧,因为Option被定义为在其类型参数上协变,因此Option[Fruit]是两个分支的超类型;主要是LUB。
好的,但是函数会发生什么情况呢?
// Implementations do not matter.
val appleFunction : Apple => Apple = ???
val orangeFunction: Orange => Orange = ???
val fruitFunction = if (true) appleFunction else orangeFunction
Run Code Online (Sandbox Code Playgroud)
在这种情况下,LUB 将(Apple with Orange) => Fruit...但为什么呢?
好吧,返回很容易,因为函数的返回也是协变的,这意味着 LUB 将再次是Fruit
但是,为什么输入不是这样呢?嗯,因为函数在其输入上是逆变的,因此要使一个函数f成为另一个函数 的子类型g, 的输入类型f必须是 的输入的超类型g;即它以相反的顺序进行,这就是为什么它被称为逆变。
这就解释了为什么编译器推断出该类型。
但是,您可能想知道差异业务是什么,为什么它很重要,以及为什么有一个看起来违反直觉的业务。但这超出了本问答的范围。
尽管如此,我可以分享一些可能有用的资源: