octavian octavian - 3 months ago 10
Scala Question

Access fields of class that's extended

I have the following model:

abstract class Shape(x1: Int, y1: Int, x2: Int, y2: Int)

case class Line(x1: Int, y1: Int, x2: Int, y2: Int) extends Shape(x1, y1, x2, y2)

case class Rectangle(x1: Int, y1: Int, x2: Int, y2: Int) extends Shape(x1, y1, x2, y2)


I am doing this processing:

val shapes: scala.collection.mutable.Queue[Shape] = mutable.Queue.empty[Shape]
shapes.foreach(shape => {
(shape.x1 until shape.x2).foreach(x => if(0 <= x && x < canvas.width && 0 <= shape.y1 && shape.y1 < canvas.height) {
board(x)(shape.y1) = 'X'
})
})


I am evaluating each
Shape
in the same way, regardless of whether it's a
Line
or
Rectangle
. However, I can't access the fields of an
abstract class
:

Error:(90, 14) value x1 is not a member of Shape
(shape.x1 until shape.x2).foreach(x => if(0 <= x && x < canvas.width && 0 <= shape.y1 && shape.y1 < canvas.height) {
^


I would make
Shape
a
case class
, but then I wouldn't be able to extend it with
Line
and
Rectangle
.

What's the most elegant way of designing the model in this case?

I think I need to allow for:


  • The extension of the base class.

  • Accessing fields of the base class.


Answer

Your problem is that construction parameters are not, by default, made available. What you need is this:

abstract class Shape(val x1: Int, val y1: Int, val x2: Int, val y2: Int)

But, wait! Why does this work then?

case class Line(x1: Int, y1: Int, x2: Int, y2: Int) extends Shape(x1, y1, x2, y2)

In the worksheet:

val line = Line(1,2,3,4)

(line.x1 until line.x2)  // Works!

The answer is that case cases export their constructor parameters, which means they have automatically provided getter methods for those parameters. Normal classes do not do this, but by specifying val or var they will. Do not use var unless you expect those parameters to be mutable, which is not recommended.