1

Is it possible to validate arguments in an overloaded constructor before calling the default one? E.g. assume that my class has a default constructor

Clazz(foo: String, bar: String)

And I would like to provide another constructor

def this(fooBar: String) {
    //validate if fooBar is of form FOO-BAR, if not, throw some custom exception, else invoke
    this(fooBar.split("-")(0), fooBar.split("-")(1))
}

Is it possible to somehow perform the validation?

jfu
  • 1,700
  • 1
  • 22
  • 31
  • This may be what you are looking for: `scala.Predef.require`: http://www.scala-lang.org/api/current/index.html#scala.Predef$ – Sudheer Aedama Jun 30 '14 at 18:09

1 Answers1

4

Auxiliary constructors in Scala have to invoke another constructor as the first thing they do, see the Scala Language Reference, Section 5.3.1, p. 74 or Scala constructor overload? So, validation of arguments in the way suggested is not possible.

See also Why can auxiliary constructors in Scala only consist of a single call to another constructor? The book "Programming in Scala" by Odersky, Spoon, and Venners sates the following on this topic (Section 6.7, p. 147):

If you’re familiar with Java, you may wonder why Scala’s rules for constructors are a bit more restrictive than Java’s. In Java, a constructor must either invoke another constructor of the same class, or directly invoke a constructor of the superclass, as its first action. In a Scala class, only the primary constructor can invoke a superclass constructor. The increased restriction in Scala is really a design trade-off that needed to be paid in exchange for the greater conciseness and simplicity of Scala’s constructors compared to Java’s. Superclasses and the details of how constructor invocation and inheritance interact will be explained in Chapter 10.

As a solution to your question, you might want to consider factory objects, see, e.g., http://fupeg.blogspot.in/2008/11/scala-constructors.html or http://alvinalexander.com/scala/factory-pattern-in-scala-design-patterns This would lead to something like the following (with a more sophisticated validation, probably):

case class Clazz(foo : String, bar : String)

case class FooBarException(msg: String) extends RuntimeException(msg)

object Clazz {
 def apply(fooBar : String) : Clazz =
     if (fooBar.count(_ == '-') == 1)   
       new Clazz(fooBar.split("-")(0), fooBar.split("-")(1))
     else
       throw new FooBarException("not valid: " + fooBar)
}

object Test extends App {
  val c1 = Clazz("foo", "bar")
  println(c1)
  val c2 = Clazz("foo-bar")
  println(c2)
  try {
    val c3 = Clazz("will throw error")
  } catch {
    case FooBarException(msg) => println(msg)
  }
}
Community
  • 1
  • 1
godfatherofpolka
  • 1,645
  • 1
  • 11
  • 24
  • That's fine, but what was wrong with validation in secondary constructors in the first place? Why the language is forbidding this? – sscarduzio Jun 30 '14 at 11:01
  • I am using factory methods right now, but I was wondering if it is possible also with constructors. But if that's impossible, I'll have to stick with multiple apply() methods in the companion object – jfu Jun 30 '14 at 11:06
  • 1
    @sscarduzio A constructor can only return a new instance. If when validating arguments the validation fails then what should the constructor return? If the behaviour is to return an `Option` or throw an exception then that behaviour should be seperated from the constructor. Doing this in the constructor also means that it would be done after the primary constructor as the primary constructor doubles as the initialiser in scala to prevent using uninitialised fields. – ggovan Jun 30 '14 at 14:48