8

Polymorphic method Back SubClass Type

 2 years ago
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.
neoserver,ios ssh client

Polymorphic method Back SubClass Type

advertisements

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


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK