我有以下代码行:
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。为什么这样做?
答案 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的实现,它不接受无类型的参数。禁止这种(非常常见的)集合实现可能被视为协方差的代价太高。