考虑以下内容(使用Scala 2.8.1和2.9.0测试):
trait Animal
class Dog extends Animal
case class AnimalsList[A <: Animal](list:List[A] = List())
case class AnimalsMap[A <: Animal](map:Map[String,A] = Map())
val dogList = AnimalsList[Dog]() // Compiles
val dogMap = AnimalsMap[Dog]() // Does not compile
最后一行失败了:
error: type mismatch;
found : scala.collection.immutable.Map[Nothing,Nothing]
required: Map[String,Main.Dog]
Note: Nothing <: String, but trait Map is invariant in type A.
You may wish to investigate a wildcard type such as `_ <: String`. (SLS 3.2.10)
Error occurred in an application involving default arguments.
val dogMap = AnimalsMap[Dog]() // Does not compile
^
one error found
将其更改为val dogMap = AnimalsMap[Dog](Map())
会修复它,但不再利用默认参数值。
为什么默认值被推断为Map [Nothing,Nothing],因为List对应物按预期工作?有没有办法创建一个使用map
arg?
编辑:我接受了对我更紧迫的第二个问题的回答,但我仍然有兴趣知道为什么Map()
的密钥类型在这些问题之间的推断不同两种情况:
case class AnimalsMap1(map:Map[String,Animal] = Map())
val dogs1 = AnimalsMap1() // Compiles
case class AnimalsMap2[A <: Animal](map:Map[String,A] = Map())
val dogs2 = AnimalsMap2[Dog]() // Does not compile
编辑2:似乎类型边界无关紧要 - 案例类的任何参数类型都会导致问题:
case class Map3[A](map:Map[String,A] = Map())
val dogs3 = Map3[Dog]() // Does not compile
答案 0 :(得分:20)
Scala有一个功能,您可以在其通用参数中定义一个协变/逆变的类。
作为协方差的一个例子:很自然地认为如果class Student extends Person
然后List[Student]
“延伸”List[Person]
。这是因为接受List[Person]
的每个方法在使用对象List[Student]
时都没有问题。这在Java中是不可能的(不使该方法也是通用的)。
逆向变形恰恰相反,解释起来有点棘手。当类型被推送到泛型类而不是读取时需要它(在List[Person]
中读取列表的元素)。一般示例是函数。函数的参数类型被放入其中,因此如果方法需要函数Person => String
,则不能使用函数Student => String
调用它(它将调用一个人的参数,但是它期待一个学生)
Scala还定义了Nothing
来隐式扩展所有内容。它是底部类型。因此,对于任何X,List[Nothing]
始终“延伸”List[X]
。List()
创建List[Nothing]
,协方差就是您编写val x: List[Person] = List()
的原因。
无论如何,Map的键类型是不变的。原因是Map[A, B]
就像一个函数A => B
,所以它只能在A
中逆变。另一种方法是考虑如果将Map[Student, String]
传递给期望Map[Person, String]
的方法会发生什么,显然它可能会尝试将Person
个对象放在那里,这是不好的,另一种方式是可以的。另一方面,Map可以被视为Iterable[(A, B)]
,这里它应该是A中的协变。因此它的值是不变的。
结果是您无法将Map[Nothing, Nothing]
分配给Map[String, Animal]
类型的变量。 Map()
创建了Map[Nothing, Nothing]
编译器告诉你:
scala> val dogs3 = Map3[Dog]()
<console>:13: error: type mismatch;
found : scala.collection.immutable.Map[Nothing,Nothing]
required: Map[String,Dog]
Note: Nothing <: String, but trait Map is invariant in type A.
You may wish to investigate a wildcard type such as `_ <: String`. (SLS 3.2.10)
Error occurred in an application involving default arguments.
val dogs3 = Map3[Dog]()
^
答案 1 :(得分:4)
只需给编译器一些帮助:
case class AnimalsMap[A <: Animal](map:Map[String,A] = Map[String, A]())
^^^^^^^^^^^
我会详细说明为什么你的解决方案不适合那些更熟悉Scala类型推理的人...
修改:有关此行为的详细解释,请参阅IttayD's answer。