将Scala与一组子类型相交

时间:2013-08-02 07:28:40

标签: scala generics

为什么这个函数不能编译?

case class MyType(n: Int)
def intersection(s1: Set[MyType], s2: Set[_ <: MyType]) =
  (s1 & s2)

我收到以下错误:

  

错误:类型不匹配;找到:设置[_ $ 1],其中类型_ $ 1   &lt;:需要MyType:scala.collection.GenSet [MyType]注意:_ $ 1&lt;:   MyType,但特性GenSet在A类中是不变的。你可能希望   调查通配符类型,例如_ <: MyType。 (SLS 3.2.10)                (w&amp; r)

是否有一种简单的方法可以“提升”第二个参数,而不使用asInstanceOf来键入Set [MyType]?

3 个答案:

答案 0 :(得分:3)

这是因为Set被定义为Set[A]。它是 in-variant 而不是共变体。

&定义为

def &(that: GenSet[A]): Set[A]

它期望和Set[A]类型的参数。但是你提供的是Set[_ <: MyType]

Set[_ <: Mytype]Set[MyType]共同变体。但正如声明所说,论证应该是变体的,即Set[MyType],因此错误。

PS:您可以将协方差视为从窄到宽的类型转换。例如:如果Dog延伸Animal而你执行Animal a = new Dog(),则会有狗(缩小)转换为动物(更广泛)。期望它不变的类型。即如果它需要Animal,则只能提供Animal。另一个例子是java.util.ArrayList,它是变体。

答案 1 :(得分:2)

Set与其类型参数不协变。

所以一个简单的解决方案是转换为List(协变):

def intersection(s1: Set[MyType], s2: Set[_ <: MyType]) =
    s1.toList.intersect(s2.toList).toSet

答案 2 :(得分:2)

Set是不变的,但是有一个非常简单的解决方法:

def intersection(s1: Set[MyType], s2: Set[_ <: MyType]) =
  s2 filter s1

或者,如果想要为结果类型获得更好的类型推断:

def intersection[X <: MyType](s1: Set[MyType], s2: Set[X]): Set[X] =
  s2 filter s1

此处s1用作函数。函数在参数中具有变异性,因此s1.apply类型(MyType) => Boolean可以接受为(_ <: MyType) => Boolean

效果与intersect相同,因为this filter thatintersect的{​​{1}}实现方式。