从Seq转换为Set并返回Seq

时间:2011-03-25 13:23:50

标签: scala

直观地说,以下内容应该有效:

case class I(i: Int)
val s = Seq(I(1),I(2),I(3))
s.sortBy(_.i)             // works
s.toSeq.sortBy(_.i)       // works
s.toSet.toSeq.sortBy(_.i) // doesn´t work

为什么它的表现不如预期?

2 个答案:

答案 0 :(得分:15)

这是混合协变和不变集合的复杂影响。 Set是不变的:Set[A]。但是Seq是协变的:Seq[+A]。现在,假设您希望在Seq中使用toSet方法。您可以尝试toSet: Set[A]。但这不起作用,因为如果AB的子类,则Seq[A]应被视为Seq[B]的子类。但是,Seq[A]仍然坚持要返回Set[A],而不是 Seq[B]的子类。所以我们的打字很糟糕。

另一方面,如果我们指定toSeq[B >: A]: Set[B],那么一切都很好:如果我们保证可以返回任何超类,那么Seq[A]可以返回Set[B]以及{{1其中Set[C]C的超类。 B承诺也会返回Seq[B]或某些Set[B],所以我们很清楚:Set[C]上的方法可以执行Seq[A]上方法的所有操作能做到。

但现在看看这个可怜的人如何面对:

Seq[B]

解决此问题的方法 - 即决定s.toSet[B >: I] .toSeq/* Type B >: I*/ .sortBy[C](/* some f:B => C */)(/* implicit ordering on C */) B并相应地输入函数和I。但它是一个非常复杂的搜索,它不仅仅是编译器现在可以处理的。因此,它要求您使用函数的输入类型来帮助它,以便它在那时知道C(然后可以将其传播回B)。

但是如果你愿意,你可以在多个层面上帮助它:

toSet

或者你可以通过向它展示在选择与早期类型的最佳匹配时不需要考虑以后的类型来帮助它:

s.toSet[I].toSeq.sortBy(_.i)
s.toSet.toSeq.sortBy[Int](_.i)

答案 1 :(得分:0)

看起来与类型推断有关,我不太清楚。

但是以下两个都可以解决问题:

s.toSet.toSeq.asInstanceOf[Seq[I]].sortBy(_.i)
s.toSet.toSeq.sortWith(_.i < _.i)