2

在 scala 应用程序中进行重构时,我遇到了从 List 更改为 Set 提出了一个我以前没有的问题的情况。我对方差有一些想法,但我想了解它对编译器的确切含义。

我有类似的东西,它编译和工作得很好:

case class MyClassList(s: List[Any])
val myList = List(("this", false)) // List[(String, Boolean)]
val listWorks = MyClassList(myList)

然后我将列表更改为设置:

case class MyClassSet(s: Set[Any])
val mySet = Set(("this", false)) // Set[(String, Boolean)]
val setFails = MyClassSet(mySet)

在这一点上,创建 MyClassSet 类型的对象不再适合我将 Set 作为参数传递,即使它接受任何 Set 也是如此。现在,当以下工作时,它有点令人困惑(请注意,该集合与之前的 mySet“相同”):

val setWorks1 = MyClassSet(Set(("this", false)))

我相信简单的解释是编译器将 mySet val 推断为一个 Set[(String, Boolean)],但是当我直接在 setWorks1 的参数列表中实例化它时,因为它接受一个 Set[Any],所以编译器是将其推断为 Set[Any]。这使得第一个示例失败,第二个示例通过。这些也有效,这表明前一个是正确的:

val setWorks2 = MyClassSet(mySet.toSet[Any])
val mySetOfAny: Set[Any] = Set(("this", false), ("that", true), ("other", false))
val setWorks3 = MyClassSet(mySetOfAny)

编译器显示的实际错误是:

Error:(15, 55) type mismatch;
found   : Set[(String, Boolean)]
required: Set[Any]
Note: (String, Boolean) <: Any, but trait Set is invariant in type A.
You may wish to investigate a wildcard type such as `_ <: (...)

列表和集合定义如下:

type List[+A]  = scala.collection.immutable.List[A]
type Set[A]    = immutable.Set[A]
  • 这种类型差异的差异是否允许我将“比任何类型更受限制的类型”的列表作为参数传递,但在 Set 的情况下不能传递?
  • 这种差异是否只会阻止类型之间的转换或转换?
  • 这主要是编译器“限制”还是不变类型的预期属性?
  • “在实践中”不变类型之间是否还有其他差异,或者它们是否归结为这样的强制转换?
4

1 回答 1

5

1)这里解释: 为什么Scala的不可变集在其类型上不是协变的?

基本上, Set[T] 也是一个Function1[T, Boolean]. Function1is的签名[-In, +Out],所以T不能同时是两者+T-T因为 scala 不允许双变量(它会显着削弱类型系统)。

.toSet[Any]2)您可以使用(这是一个包装器)轻松地投射它asInstanceOf。还有一种方法可以跳过方差检查

3, 4) 它是泛型(多态)类型的预期属性。它们可以是不变的/协变的/逆变的(不仅),而且它由简单的规则正式描述。你可以在这里阅读解释:https ://stackoverflow.com/a/27627891/1809978

于 2016-10-13T10:29:23.183 回答