Scala Set [_] vs Set [Any]

Pol*_*let 5 scala

我有以下代码行:

case set: Set[Any] => setFormat[Any].write(set)
Run Code Online (Sandbox Code Playgroud)

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

非变量类型参数类型模式中的任何类型scala.collection.Set [Any]未选中,因为它被擦除消除[warn]

很公平.

所以我改变了我的路线:

case set: Set[_] => setFormat[Any].write(set)
Run Code Online (Sandbox Code Playgroud)

现在我收到一个错误:

[错误]发现:scala.collection.Set [_]

[error] required:scala.collection.Set [Any]

Q1.这两者有什么区别?

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

case set: Set[_] => setFormat[Any].write(set.map(s => s))
Run Code Online (Sandbox Code Playgroud)

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

Q2.这为什么有用?

Did*_*ont 12

Q1:A Set[Any]是一个元素类型为Any的Set.A Set[_]是一个元素类型未知的Set.也许这是一个Set[Int],也许是一个Set[String],也许是一个Set[Any].与大多数(不可变的)集合相反,Set不是协变的(声明是trait Set[A],不是trait Set[+A]).所以a Set[String] 不是a Set[Any],更一般地说,你不能保证你不知道的元素类型的集合(即Set[_])是a Set[Any].

Q2:它的工作原理是因为无论集合元素的(未知)类型A,身份函数s => s都可以被认为是函数A => Any.(方差为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
Run Code Online (Sandbox Code Playgroud)

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

注意:关于Set不协变:这是不幸的,因为人们非常感觉一组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的实现,它不接受无类型的参数.禁止这种(非常常见的)集合实现可能被视为协方差的代价太高.