2013-01-19 113 views
5

在斯卡拉(2.10)如果我要求一个List(1.0, 2)然后我得到一个List[Double],如预期。意外的斯卡拉地图类型

但是......

如果我问一个Map(1.0 -> 'A', 2 -> 'B')我得到一个Map[AnyVal, Char]。我想要的钥匙是Double。询问Map[Double, Char](1.0 -> 'A', 2 -> 'B)给出'2'类型不匹配。

这个我觉得很混乱!这不是不一致吗?

除外:List[(Double, Char)]((1.0, 'A'), (2, 'B')).toMap虽然给了我一个地图[双,字]。

+0

可以在2.9.2上重现。 –

回答

6

如果你看看Scala的type hierarchy,你可以看到,基本类型不是在一个子类/超类型的关系。但是,有一种称为数字扩展的机制,例如允许您调用一个采用Double参数的方法,例如传入一个Int。然后Int自动“加宽”到Double

这就是List(1.0, 2)给你的原因List[Double]

Map构造函数需要Tuple[A, B]参数。数字加宽不适用于高阶类型,因此如果您混合使用数字类型,则目标类型推断不适用于您。

case class Test[A](tup: (A, Char)*) 
Test(1.0 -> 'A', 2 -> 'B') // AnyVal 

此外,箭头操作->会挡住你的路:

Test[Double](2 -> 'B') // found: (Int, Char) required: (Double, Char) 

这是类型推断,我认为的另一个限制。写一个元组a -> b(a, b)的语法糖,由Predef上的隐式方法any2ArrowAssoc提供。如果没有这种间接,如果你直接构建Tuple2,它的工作原理:

Test[Double]((2, 'B')) 

所以数字加宽仍然无法正常工作,但至少可以强制类型:

Map[Double, Char]((1.0, 'A'), (2, 'B')) 

显示数字加宽工作的最后一个示例:

def map[A, B](keys: A*)(values: B*) = Map((keys zip values): _*) 
map(1.0, 2)('A', 'B') // Map[Double, Char] 
3

对于没有类型声明的List,Scala会查看所有元素并尝试查找常用类型。在你的情况下,由于Int可以转换为Double,它会将混合列表转换为一个List [Double]来提升你的一个Int。

Map构造函数需要一系列2元组。你会得到相同的行为,如果你只是构造元组的列表:

scala> List((1, "one"), (2.0, "two.oh")) 
res0: List[(AnyVal, String)] = List((1,one), (2.0,two.oh)) 

Tuple2 [诠释,字符串]不能自动晋升为Tuple2 [双,字符串。在这种情况下,你需要帮助编译器了一点与类型声明:

scala> val x: List[(Double, String)] = List((1, "one"), (2.0, "two.oh")) 
x: List[(Double, String)] = List((1.0,one), (2.0,two.oh)) 

scala> val x = List[(Double, String)]((1, "one"), (2.0, "two.oh")) 
x: List[(Double, String)] = List((1.0,one), (2.0,two.oh)) 

或在您的情况:

scala> val x = List[(Double, String)]((1, "one"), (2.0, "two.oh")).toMap 
x: scala.collection.immutable.Map[Double,String] = Map(1.0 -> one, 2.0 -> two.oh) 

出于某种原因,在Map上使用类型声明不起作用。不知道为什么:

scala> val x = Map[Double, String](1 -> "one", 2.0 -> "two.oh") 
<console>:7: error: type mismatch; 
found : (Int, String) 
required: (Double, String) 
    val x = Map[Double, String](1 -> "one", 2.0 -> "two.oh")