Search code examples
scalainheritancemethod-chaining

Inherit class with chaining functions


Lets say i have a class with chaining functions

class Vehicle {
  protected var position: (Int, Int) = (0, 0)
  def moveLeft(meters: Int): Vehicle = {
    position = position._1 - meters -> position._2
    this
  }

  def moveForward(meters: Int): Vehicle = {
    position = position._1 -> (position._2 + meters)
    this
  }
}

so each method returns instance itself

Now i want to inherit Vehicle and add some method to new class

class Helicopter extends Vehicle {
  protected var verticalDimension: Int = 0
  def flyIntoTheSky(meters: Int): Helicopter = {
    verticalDimension += meters
    this
  }
}

If i will create a new class instance and call any functions from parent, for example

new Helicopter().moveLeft(10).moveForward(20)

i will not be able to call new class method flyIntoTheSky because methods return type is Vehicle, and Vehicle does not know anything about methods of Helicopter.

2 obvious ways to solve it: Either using asInstanceOf

new Helicopter()
  .moveLeft(10)
  .moveForward(20).asInstanceOf[Helocopter]
  .flyIntoTheSky(10000)

either overriding each and avery parental method in new class

Both ways are not looking nice, i would prefer to leave all this type issues to Vehicle class and forget about it, so i found(as i hoped) the solution, rewriting methods like this

def moveLeft[T](meters: Int, vehicle: T = this): T = {
  position = position._1 - meters -> position._2
  this.asInstanceOf[T]
}

so i expected that return type will be taken from method's second parameter vehicle, which always equals to default value "this", and will return the value of current type. But unfortunately

new Helicopter().moveLeft(10)

still returns value of type Vehicle res0: Vehicle = Helicopter@1de81c37

So the first question: why it doesn't work as i expected. And the second: is there are any beautiful way to solve this problem

Thank you

PS In google i found this solution in Java https://www.andygibson.net/blog/article/implementing-chained-methods-in-subclasses/ , but i dont know java and can not translate it in scala


Solution

  • If the method always returns this you can give it return type this.type.

    def moveLeft(meters: Int): this.type = {
      position = position._1 - meters -> position._2
      this
    }