Concise way to implement the same function for a subclass return type

Viewed 48

I have a multiplication operator override in my base class

open class Tuple(
    open val x: Double,
    open val y: Double,
    open val z: Double,
    open val w: Double
) {
    
    open operator fun times(scalar: Double) = Tuple(x * scalar, y * scalar, z * scalar, w)
}

I would like for this operator override to work exactly the same for subclasses

data class Point(
    override val x: Double,
    override val y: Double,
    override val z: Double
) : Tuple(x, y, z, 1.0) {
   
    //Attempt 1
    override operator fun times(scalar: Double) = Point(x * scalar, y * scalar, z * scalar)


    // Attempt 2
    private fun toPoint(other: Tuple) = Point(other.x, other.y, other.z)
    operator fun times(scalar: Double) = toPoint(super.times(scalar))
}

but to instead return the subclass, which requires a subset of parameters of the base class in order to be instantiated.

Is there a way where I can write just one function and have it return the corresponding type based on the input class type?

1 Answers

I don't think it's possible. The main reason is, your operator fun creates a new instance of Tuple. In the Tuple class, it has no idea how many sub-classes or which sub-classes it has. Therefore, it cannot create a new instance of its subclass, and it doesn't know how to create.. well it can receive a class object and create an instance with reflection. But it's not what you are looking for I guess.

Another example is that Object is the superType of all classes. You cannot in Object add a method (if you could) to expect that it can magically create an instance of any type.

However, if you made your classes mutable( all required val variables -> var), and make the operator fun update the values instead of creating new objects, you can do in this way:

In Tuple:

open operator  fun  times(scalar: Double) = 
    this.also {x *= scalar; y *= scalar; z *= scalar; w}

In Point, you don't need do anything special. Then you can do:

val p = Point(1.0,2.0,3.0)
print(p*10.0)

It prints Point(x=10.0, y=20.0, z=30.0)

If you do need them to be immutable, you can extract those operator functions to a file:

operator  fun  Tuple.times(scalar: Double) :Tuple= Tuple ( x * scalar, y * scalar, z * scalar, w )
operator  fun  Point.times(scalar: Double) :Point= Point ( x * scalar, y * scalar, z * scalar)
Related