Manu Chadha Manu Chadha - 15 days ago 4
Scala Question

canEqual in List returns true for Set argument

question 1 - Why does this code return true when I compare a List with Set (different elements) but returns false when I compare List with List or Set of?

scala> val l = List(1,2,3)
l: List[Int] = List(1, 2, 3)

scala> val l2=List(1,2,3)
l2: List[Int] = List(1, 2, 3)

scala> val l3=List(1,3,3)
l3: List[Int] = List(1, 3, 3)

scala> l.canEqual(l2)
res2: Boolean = true

/*I guess this returns true becuase canEqual check for type of the passed instance. Something like def canEqual(other: Any): Boolean = other.isInstanceOf[List] */

scala> l.canEqual(l3)
res3: Boolean = true

//but if canEqual checks for type of passed instance then why this returns true when I call canEqual on List but pass a Set

scala> val s = Set(1,2,3)
s: scala.collection.immutable.Set[Int] = Set(1, 2, 3)

scala> l.canEqual(s)
res4: Boolean = true

scala> val s2=Set(2,3,4)
s2: scala.collection.immutable.Set[Int] = Set(2, 3, 4)

scala> l.canEqual(s)
res5: Boolean = true

scala> l.canEqual(s2)
res6: Boolean = true


Probably canEqual in List doesn't check for instance List type but some superclass instance or probably doesnt override equals. Is this the case?

Question 2 - I read that canEquals is used with equals and hashcode to define if an instance is equal to some other instance. canEquals is generally called by equals. Is there a usecase when a user will call canEqual directly from code (Like I am trying to do above)?

Answer

Let's start with simpler example:

scala> "aaaa".canEqual(5)
res0: Boolean = true

So everything can be equal by default - but it doesn't mean that it will be. The real use case of canEqual is disabling equivalence (see this article):

canEqual method for non-final classes. This allows subclasses to override canEqual if they want to not allow being equal to the parent class or sibling classes.

So the whole reason of canEqual is inheritance (precicely disabling equality in siblings/parents). It is not recommended to call it outside of equals as it violates Liskov Substitution Principle as it stated in the article above.

IMHO, I would not recommended to use at all, it's counter-intuitive and dangerous. If you need to disable equality between entities it's better to prefer composition to inheritance, as inheritance logically requires inheritance of equality, but composition is more flexible.