3

I have the following class model:

sealed abstract class Tile(val coordinate: Int, val isOccupied: Boolean) {
  def isEmpty() : Boolean
  def getPiece() : Option[Piece]
}

case class EmptyTile(coordinate: Int) extends Tile(coordinate, false) {
  override def toString: String = "" +coordinate
  override def isEmpty() = true
  override def getPiece() = None
}

case class OccupiedTile(coordinate: Int, val piece: Piece) extends Tile(coordinate, true) {
  override def toString = piece.toString
  override def isEmpty = false
  override def getPiece = Some(piece)
}

and I get the following error:

Error:(6, 22) overriding value coordinate in class Tile of type Int;
 value coordinate needs `override' modifier
case class EmptyTile(coordinate: Int) extends Tile(coordinate, false) {
                 ^

What am I doing wrong?

EDIT: Request to see Piece class, adding here:

import Alliance.Alliance
import PieceType.PieceType

abstract class Piece(val piecePosition: Int, val pieceType : PieceType, val alliance: Alliance) extends Movable {
}

object PieceType extends Enumeration {
  type PieceType = Value
  val PAWN, KNIGHT, BISHOP, ROOK, QUEEN, KING = Value
}
Amir Afghani
  • 37,814
  • 16
  • 84
  • 124

2 Answers2

5

Your abstract class Tile declares a val coordinate, making this value publicly accessible. Your case class EmptyTile implicitly declares coordinate as a val as well (case class "magic"). Basically, your case class is effectively trying to override a value already provided by your abstract class.

You can either remove the val in your abstract class declaration, or not make EmptyTile and OccupiedTile case classes.

Edit: proposed alternative after comment:

trait Tile {
  def coordinate: Int
  def isOccupied: Boolean
  def isEmpty() : Boolean = !isOccupied
  def getPiece() : Option[Piece]
}

case class EmptyTile(coordinate: Int) extends Tile {
  override def toString: String = "" +coordinate
  val isOccupied = false
  def getPiece() = None
}

case class OccupiedTile(coordinate: Int, val piece: Piece) extends Tile {
  override def toString = piece.toString
  val isOccupied = true
  def getPiece = Some(piece)
}
serv-inc
  • 35,772
  • 9
  • 166
  • 188
vptheron
  • 7,426
  • 25
  • 34
  • If I remove the val in my abstract class - will it be a var? Or will it simply not exist in the base class? I suppose I can get rid of the case classing, but this really sucks... – Amir Afghani Jul 11 '14 at 18:21
  • @AmirAfghani No, it won't be a `var`, it just won't be used since the constructor isn't actually doing anything with it. If you want it accessible in `Tile`, I'd recommend removing it from the constructor and declaring `def coordinate: Int` (abstract) within it. Then you can keep your case classes. – Michael Zajac Jul 11 '14 at 18:30
  • By passing in coordinate as an Int, are you implicitly overriding the def declaration in the trait? – Amir Afghani Jul 11 '14 at 18:36
  • `coordinate` is undefined in the trait. Since it is a constructor argument of the case classes, you automatically implement it - not override, since the trait does not give any value to the member. The fact that it is an Int does not change anything. – vptheron Jul 11 '14 at 20:48
1

case class automatically makes its arguments vals. This is why the argument coordinate to EmptyTile, understood as val coordinate, conflicts with the abstract class's val coordinate.

One way to fix this is to have coordinate and isOccupied be defined as abstract in Tile. Tile can even be a trait instead of an abstract class:

sealed trait Tile {
  def coordinate: Int
  def isOccupied: Boolean
  ...
}

case class EmptyTile(coordinate: Int) extends Tile {
  def isOccupied = false
  ...
}

case class OccupiedTile(coordinate: Int, piece: Piece) extends Tile {
  def isOccupied = true
  ...
}
Dan Getz
  • 8,774
  • 6
  • 30
  • 64
  • thanks for your answer. Doesn't it seem wrong though to have Tile be a trait vs a 'noun' like thing? It seems like abstract classes with case class overrides are just not developer friendly in Scala. +1 for the answer – Amir Afghani Jul 11 '14 at 18:33
  • Another way to look at it is that in your case, the functionality you need from `Tile` could be completely specified as a trait (common interface), so why use a class when a trait would work just as well? See http://stackoverflow.com/q/1991042/ – Dan Getz Jul 11 '14 at 18:53
  • 1
    In terms of "noun" vs. "trait", you can read `EmptyTile extends Tile` as "`EmptyTile` is a `Tile`, and a `Tile` is something that has these properties"... I'm not sure what's not-noun-like about that. – Dan Getz Jul 11 '14 at 18:55
  • I suppose you're right. I was viewing traits as interfaces - which perhaps is wrong – Amir Afghani Jul 11 '14 at 19:00
  • I think it's correct to view them as interfaces, which is exactly why they work well for your `Tile`. – Dan Getz Jul 11 '14 at 22:45