Am I getting completely wrong here ? Should I use a different pattern maybe using override modifier ?
Yes. There are two alternatives:
sealed trait Foo {
val a: String
def withField(b: String): Foo
}
case class Bar1(a: String) extends Foo {
// return types are covariant, and Bar1 is subtype of Foo,
// so this is legal
def withField(b: String): Bar1 = ...
}
or
sealed trait Foo[ThisType <: Foo[ThisType]] {
val a: String
def withField(b: String): ThisType
}
case class Bar1(a: String) extends Foo[Bar1] {
def withField(b: String): Bar1 = ...
}
Note the second is more complex, and should only be used if you actually need it.
EDIT to answer Christian's question:
sealed trait Foo {
type ThisType <: Foo
def withField(b: String): ThisType = (this match {
case b1: Bar1 => b1.copy(a = b)
...
}).asInstanceOf[ThisType]
}
case class Bar1(a: String) extends Foo {
type ThisType = Bar1
}
I don't like it: it needs a cast, actually using it would require dependent method types, and I wouldn't be surprised if it broke down in practice (e.g. because the compiler can't prove foo.ThisType
and foo.withField("a").ThisType
are the same).