Yes, they are merged, but there’s a special rule - if there’s an explicitly defined method then compiler doesn’t provide its own generated definition. So e.g. case class X(a: Int, b: String) expands to:
class X(val a: Int, val b: String) {
def equals ...
def hashCode ...
def toString ...
...
}
object X {
def apply(a: Int, b: String): X = X(a, b)
def unapply ...
}
but if you e.g. provide your own toString then your implementation won’t be replaced by autogenerated one:
case class X(a: Int, b: String) {
override def toString(): String =
"hey, this method is explicitly defined so compiler won't regenerate it!"
}
This rule applies to both class and it’s companion object.
We’ve already used that rule here: What does Equivalence of instances mean? - #11 by tarsa to provide own equals and hashCode definitions instead of the auto-generated ones.
If you look at generated .class files then you’ll notice that object Xxx compiles to two files: Xxx.class and Xxx$.class. OTOH class Xxx compiles to just Xxx.class. So every time you have a class (no matter if case or not) with its companion object the Xxx.class contains definitions related to both class and object. In other words - merging happens quite often.