Intersect two sets, of a class and of a derived class

Viewed 477

I have two sets, one of them lists instances of a derived class. The set type as created is out of my control, I cannot make sure both are Set[A] instead. The set23 is used in other contexts where I need its type to be Set[B]. I would like to intersect:

case class A(name: String)
class B(name: String) extends A(name)

val set12 = Set(new A("1"), new A("2"))
val set23 = Set(new B("2"), new B("3"))

set12 intersect set23 // does not work

Error is:

type mismatch; found : scala.collection.immutable.Set[B]

required: scala.collection.GenSet[A]

Note: B <: A, but trait GenSet is invariant in type A.

I am aware Set is not covariant, is there some clean workaround without rebuilding the sets? Following works, but I would prefer not to use asInstanceOf:

set12 intersect set23.asInstanceOf[Set[A]]
1 Answers

When you create the set as Set(new A("1"), new A("2")), it assignes the type Set[A] to the resulting value. When you create the set as Set(new B("2"), new B("3")) it assignes the type Set[B] to the resulting value.

Therefore, the resulting sets are different types and can not be intersected. But if you initialize your variables as follows:

val set12: Set[A] = Set(new A("1"), new A("2"))
val set23: Set[A] = Set(new B("2"), new B("3"))

then, you are assigning the types yourself. Since the resulting values have the same type, you are able to intersect and do the other operations.

For example, you can use intersect like the following:

val as: Set[A] = set12 intersect set23  // gives empty set as intersection

In the example above, the resulting intersection set is empty since they don't have the same objects.

To achieve successful comparison, you should also override the equals method of classes. In your case, it is enough to override only A. Below is an unrecommended but working way (since extending case classes is not encouraged):

case class A(name: String) {
  override def equals(obj: scala.Any): Boolean = obj match {
    case A(x) => x.equals(name)
    case _ => false
  }
}

class B(name: String) extends A(name)

val set12: Set[A] = Set(new A("1"), new A("2"))
val set23: Set[A] = Set(new B("2"), new B("3"))

val as: Set[A] = set12 intersect set23  // gives A(2) as intersection
println(s"as = ${as}")
Related