考虑
scala> val m = Map('a -> 3, 'b -> 4)
m: scala.collection.immutable.Map[Symbol,Int] = Map('a -> 3, 'b -> 4)
scala> val d: Double = m.getOrElse('c, 0)
<console>:8: error: type mismatch;
found : AnyVal
required: Double
val d: Double = m.getOrElse('c, 0)
^
scala> m.getOrElse('c, 0)
res0: Int = 0
scala> m.getOrElse('a, 0)
res1: Int = 3
为什么Scala认为getOrElse
调用会返回AnyVal
,即使它显然会返回Int
?
此外,即使失败也会出现同样的错误:
scala> val x: Double = m.getOrElse('a, 0): Double
<console>:8: error: type mismatch;
found : AnyVal
required: Double
val x: Double = m.getOrElse('a, 0): Double
然而这有效:
scala> val x: Double = m.getOrElse('a, 0): Int
x: Double = 3.0
这发生在2.11.x上;我没有在2.10.x上尝试过它。
答案 0 :(得分:8)
getOrElse
的签名是:
def getOrElse[B1 >: B](key: A, default: ⇒ B1): B1
发表声明
val d: Double = m.getOrElse('c, 0)
您告诉Scala B1
应该是Double
。但Double
不是Int
的超类型,而常见的超类型是AnyVal
。因此,你不能做那个任务......
尝试将Int
结果转换为Double
:
val d /* : Double */ = m.getOrElse('c, 0).toDouble
答案 1 :(得分:6)
因为map在其第二个类型参数中是协变的,所以我们需要使用特殊技巧来避免逆变问题,即指定此类型的上限:
def getOrElse[B1 >: B](key: A, default: => B1): B1
因为您明确规定返回类型为Double
,scalac会将其常用超类型解析为AnyVal
,因为Int
和Double
是AnyVal
的子类型。在第二种情况下,scalac会看到类型归属,因此它会正确推断B1
,然后将Int转换为Double。
答案 2 :(得分:5)
我正在努力改善阅读-Ytyper-debug
输出。
使用预期类型Double
,0
的类型将被视为Double
,0.0
。那是因为人们讨厌的转换次数越来越多。
这是在解决B1
之前。 B1
被视为AnyVal
,因为这是A
/ Int
和Double
的润滑。
| |-- Double TYPEmode (site: value d in $iw)
| | \-> Double
| |-- m.getOrElse(scala.Symbol("c"), 0) : pt=Double BYVALmode-EXPRmode (site: value d in $iw)
| | |-- m.getOrElse BYVALmode-EXPRmode-FUNmode-POLYmode (silent: value d in $iw)
| | | |-- m EXPRmode-POLYmode-QUALmode (silent: value d in $iw)
| | | | \-> m.type (with underlying type scala.collection.immutable.Map[Symbol,Int])
| | | [adapt] [B1 >: B](key: A, default: => B1)B1 adapted to [B1 >: B](key: A, default: => B1)B1
| | | \-> (key: Symbol, default: => B1)B1
| | |-- scala.Symbol("c") : pt=Symbol BYVALmode-EXPRmode-POLYmode (site: value d in $iw)
| | | |-- scala.Symbol BYVALmode-EXPRmode-FUNmode-POLYmode (silent: value d in $iw)
| | | | |-- scala.Symbol.apply BYVALmode-EXPRmode-FUNmode-POLYmode (silent: value d in $iw)
| | | | | \-> (name: String)Symbol
| | | | [adapt] Symbol.type adapted to (name: String)Symbol
| | | | \-> (name: String)Symbol
| | | |-- "c" : pt=String BYVALmode-EXPRmode (silent: value d in $iw)
| | | | \-> String("c")
| | | \-> Symbol
| | |-- 0 : pt=Double EXPRmode-POLYmode (site: value d in $iw)
| | | \-> Double(0.0)
| | solving for (B1: ?B1)
<console>:8: error: type mismatch;
found : AnyVal
required: Double
val d: Double = m.getOrElse('c, 0)
^
| | \-> <error>
此外,-xprint:typer
显示
m.getOrElse[AnyVal](scala.Symbol.apply("c"), 0.0);
使用预期的param类型对args进行类型检查的spec explains(案例3方法),这就是为什么你在那里转换到Double
,而不是让方法返回{ {1}}在作业中扩展为Int
。