Polymorphic method Back SubClass Type
source link: https://www.codesd.com/item/polymorphic-method-back-subclass-type.html
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
Polymorphic method Back SubClass Type
Suppose I have a superclass with multiple subclasses. For each of these classes I would like to have an updatePropertyX
method which updates the property x
and returns an new instance of that class.
Additionally I would like the superclass to be abstract and I would like to only implement this updatePropertyX
method once.
Here is what I have tried so far:
class Super(val name: String, val x: String)
{
def identify = println("This is a Super with properties" +
s"\n\tName: ${this.name}\n\tData: ${this.x}")
def updateX(newX: String): Super = new Super(name, newX)
}
class Sub_1(name: String, x: String) extends Super(name, x)
{
override def identify = println("This is a Sub_1 with properties" +
s"\n\tName: ${this.name}\n\tData: ${this.x}")
}
class Sub_2(name: String, x: String) extends Super(name, x)
{
override def identify = println("This is a Sub_2 with properties" +
s"\n\tName: ${this.name}\n\tData: ${this.x}")
}
val s1 = new Sub_1("sub1", "original data")
s1.identify
/*
This is a Sub_1 with properties
Name: sub1
Data: original data
*/
val s2: Sub_1 = s1.updateX("new data")
However there is a type mismatch error on the last line: found Super
, expected Sub_1
(also Super
is not abstract as I would like).
I also tried pulling the method out:
def updateSubX[T <: Super](orig: T, newX: String): T = new T(orig.name, newX)
val s2 = updateSubX(s1, "new data")
But this is problematic because I don't think you can instantiate a class based on a type parameter (because erasure?).
Any thoughts on how I could get this to work?
So you could use F-bounded polymorphism to return the correct subtype:
abstract class Super[A <: Super[A]](val name: String, val x: String) {
abstract def updateX(newX: String): A
}
The type is weird but it does the job.
Now your subclasses look like:
class Sub_1(name: String, x: String) extends Super[Sub_1](name, x)
Unfortunately without knowing the constructor of the subclass, you cannot actually implement the updateX
method generically and will have to leave it abstract for subclasses to implement, which is awkward (at least it is enforced though). Since the constructors could take different parameters though, I don't see another (non-reflective) way.
A broader question is: What are you trying to achieve with this? If all your subclasses consist of the same immutable data, what is different?
If it is behaviour only (as in your identify
method), would dropping inheritance and having only one class with with identify being passed in as a function instead work? Something like:
class MyClass(val name: String, val x: String, val identify: () => Unit)
That may not be appropriate, depending on what you are trying to achieve
Recommend
About Joyk
Aggregate valuable and interesting links.
Joyk means Joy of geeK