Optimight
Optimight

Reputation: 3061

parameter validation during constructor invocation and after object creation

I would like to check constructor arguments and refuse to construct throwing IllegalArgumentException in case the arguments set is not valid (the values don't fit in expected constraints). And same check should work while setting same argument set while modifying the object. How to code this in Scala?

  scala> class Rectangle (var Length:Double, var Width:Double){
   | if (Length <0)
   | throw new IllegalArgumentException("The Length must be Non-negative")
   | if (Width <0)
   | throw new IllegalArgumentException("The Width must be Non-negative")
   | def setLength(l:Double) = Length = l
   | }
  defined class Rectangle

  scala> var R = new Rectangle (-9, -9)
  java.lang.IllegalArgumentException: The Length must be Non-negative
at Rectangle.<init>(<console>:9)

  scala> var R = new Rectangle (0, -9)
  java.lang.IllegalArgumentException: The Width must be Non-negative
at Rectangle.<init>(<console>:11)



   scala> var R = new Rectangle(9, 9)
   R: Rectangle = Rectangle@1590164


   scala> R.Length
   res7: Double = 9.0

   scala> R.Width
   res8: Double = 9.0

   scala> R.setLength(18)

   scala> R.Length
   res10: Double = 18.0

   scala> R.setLength(-9)
   // R.setLength should not the set the Length to -9.   **************************
   scala> R.Length
   res12: Double = -9.0

Upvotes: 1

Views: 1874

Answers (4)

kiritsuku
kiritsuku

Reputation: 53348

I don't like the answers mentioned so far. Your problem is that parameters must be valid to create an object. The other solutions abort object creation but in my opinion what you really want is not an abortion but some checks before creation of the object. To realize this you need factories:

object Rectangle {
  def apply(length:Double, width:Double): Rectangle = {
    require(length >= 0, "The Length must be Non-negative")
    require(width >= 0, "The Width must be Non-negative")
    new Rectangle(length, width)
  }
}
class Rectangle private(val length:Double, val width:Double) {
  def length_=(l: Double) = Rectangle(l, width)
}

The only problem here is, that we are not allowed to create a case class when we want to define our own apply-Method (with same method signature as the class constructor).

Upvotes: 2

senia
senia

Reputation: 38045

Mutable state is a bad idea, but if you do need it and don't want two points of validation, you can use a slightly modified solution of Prince John Wesley:

class Rectangle(l: Int, w: Int) {

  private[this] var _length: Int = _
  private[this] var _width: Int = _
  def length: Int = _length
  def width: Int = _width
  def length_=(len: Int) { require(len > 0, "Invalid length: " + len + " < 0"); _length = len }
  def width_=(wid: Int) { require(wid > 0, "Invalid width: " + wid + " < 0"); _width = wid }

  length = l
  width = w

  override def toString = length + ", " + width
}

Upvotes: 2

Debilski
Debilski

Reputation: 67858

Must it be mutable?

case class Rectangle(length:Double, width:Double) {
  if (length < 0)
    throw new IllegalArgumentException("The Length must be Non-negative")
  if (width < 0)
    throw new IllegalArgumentException("The Width must be Non-negative")
  def setLength(l: Double) = copy(length=l)
}

Upvotes: 2

Prince John Wesley
Prince John Wesley

Reputation: 63698

You need a property setter/getter methods.

class Rectangle(private var l: Int, private var w: Int) {
  require(l > 0, "Invalid length: " + l + " <0 ")
  require(w > 0, "Invalid width: " + w + " <0 ")
  def length = l
  def width = w
  def length_=(len: Int) { require(len > 0, "Invalid length: " + len + " < 0"); l = len }
  def width_=(wid: Int) { require(wid > 0, "Invalid width: " + wid + " < 0"); w = wid }
  override def toString = length + ", " + width
}

Upvotes: 2

Related Questions