I need a way to enforce a method in an abstract class to have a return type of the concrete class of the object it is called on. The most common example is a copy
However, none of them really forces a implementation to return its own type. For example, the following classes would be valid.
But isn't it normal? Otherwise it would mean that you could not merely extend A
to add a new method by example, as it would automatically break the contract that you are trying to create (that is, the new class's copy
would not return an instance of this class, but of A
).
The very fact of being able to have a perfectly fine class A
that breaks as soon as you extend it as class B
feels wrong to me.
But to be honest I have trouble putting words on the problems it causes.
UPDATE: After thinking a bit more about this, I think this could be sound if the type check ("return type == most-derived class") was made only in concrete classes and never on abstract classes or traits. I am not aware of any way to encode that in the scala type system though.
The fact that I can do that causes that, if I am doing copies of objects of which the only information I have is that they are of a given subclass of A's
Why can't you just return a Seq[Ca#Self]
? By example, with this change passing a list of B
to createCopies
will as expected return a Seq[B]
(and not just a Seq[A]
:
scala> def createCopies[CA <: A](seq: Seq[CA]): Seq[CA#Self] = seq.map(_.copy(123))
createCopies: [CA <: A](seq: Seq[CA])Seq[CA#Self]
scala> val bs = List[B]( new B(1, "one"), new B(2, "two"))
bs: List[B] = List(B@29b9ab6c, B@5ca554da)
scala> val bs2: Seq[B] = createCopies(bs)
bs2: Seq[B] = List(B@92334e4, B@6665696b)