【问题标题】:Scala Set[_] vs Set[Any]Scala Set[_] 与 Set[Any]
【发布时间】:2015-03-05 16:17:57
【问题描述】:

我有以下代码行:

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

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

非可变类型参数 Any 类型模式 scala.collection.Set[Any] 未被选中,因为它被 擦除 [警告]

很公平。

所以我将我的行改为:

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

现在我得到一个错误:

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

[错误] 必需: scala.collection.Set[任何]

第一季度。这两者有什么区别?

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

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

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

第二季度。为什么会这样??

【问题讨论】:

  • 你能给出setFormat的类型/签名并写吗?
  • 只是好奇,删除明确的[Any] 和地图,如我在“备注”下的回答中建议的那样,是否适用于您的情况?

标签: scala


【解决方案1】:

Q1Set[Any] 是一个 Set,其元素类型为 Any。 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 => Any。 (方差为Function1[-T1, +R]。然后,可以根据需要将生成的set.map(s => s) 键入为Set[Any]

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

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

但不允许在调用站点显示(例如f[Any](x)),因为我们不知道X 是否是X[Any]

注意:关于 Set 不是协变的:这很不幸,因为人们非常认为一组 Cats 也是一组 Animals。这是其中一个原因(可能还有其他原因)。

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 的实现,它不接受无类型参数。禁止这种(非常常见的)集合实现可能会被视为协方差的代价太高。

【讨论】:

猜你喜欢
  • 2011-06-04
  • 1970-01-01
  • 1970-01-01
  • 2014-10-03
  • 2012-05-26
  • 2011-02-22
  • 2022-11-20
  • 1970-01-01
  • 2010-11-30
相关资源
最近更新 更多