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)
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] */
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)
res4: Boolean = true
scala> val s2=Set(2,3,4)
s2: scala.collection.immutable.Set[Int] = Set(2, 3, 4)
res5: Boolean = true
res6: Boolean = true
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.