Scala Set [_] vs Set [Any]

时间:2015-03-04 23:12:46

标签: scala

我有以下代码行:

case set: Set[Any] => setFormat[Any].write(set)

但是,编译器会发出警告:

  

非变量类型参数类型模式中的任何一种   scala.collection.Set [Any]未经检查,因为它已被删除   擦除[警告]

足够公平。

所以我将我的行改为:

case set: Set[_] => setFormat[Any].write(set)

现在我收到一个错误:

  找到

[error]:scala.collection.Set [_]

     

[错误]要求:   scala.collection.Set [任意]

Q1。这两者有什么区别?

然后我将代码更改为以下内容:

case set: Set[_] => setFormat[Any].write(set.map(s => s))

现在很高兴没有任何错误或警告。

Q2。为什么这样做?

1 个答案:

答案 0 :(得分:12)

Q1 Set[Any]是一个元素类型为Any的Set。 Set[_]是一个元素类型未知的集合。也许它是Set[Int],也许是Set[String],也许是Set[Any]。与大多数(不可变的)集合相反,Set不是协变的(声明是trait Set[A],而不是trait Set[+A])。因此Set[String]不是Set[Any],更一般地说,您不能保证您不知道的元素类型(即Set[_])的集合是Set[Any]

Q2 :它起作用,因为无论集合的元素的(未知)类型A是什么,身份函数s => s都可以被认为是函数A =>任何。 (方差为Function1[-T1, +R]。然后,生成的set.map(s => s)可以根据需要输入为Set[Any]

备注:如果没有setFormat和write的定义很难确定,但是你真的需要明确[Any]中的setFormat[Any]类型参数吗?人们可以将存在主义传递给通用函数,即

val x: X[_] = ....
def f[A](xa: X[A]) = ...
f(x) // allowed

但不允许在呼叫网站上明确说明(例如f[Any](x)),因为我们不知道X是否为X[Any]

注意:关于Set not is covariant :这很不幸,因为很多人觉得一组Cats也是一组动物。这是一个原因(可能还有其他原因)。

Set有一个方法def contains(a: A): Boolean,此签名可防止协方差。其他集合有def contains[A1 >: A](a: A): Boolean,它允许协方差,但实际上等同于def contains(a: Any): Boolean

它的工作原理,因为实现基于方法equals,可以在任何地方使用(随JVM一起提供),并采用类型为Any的参数。使用与列表内容无关的类型的值进行调用很可能是一个错误,并且更受限制的签名会更好,但是为协方差付出的代价很小。

contains这个宽松的签名限制了实现基于equals(也可能hashCode)。它不适用于基于Ordering的实现,它不接受无类型的参数。禁止这种(非常常见的)集合实现可能被视为协方差的代价太高。