Monday, November 2, 2009

Multiple Constructors

In Scala there is a primary constructor: class MyClass(constructorParam:Any). Unlike Java, that constructor must be called. The question that often arises is, "How can one define multiple constructors?" There is a simple way to do this, however often a factory companion object can be used to remove the need for multiple constructors. Factory Companion Objects are covered in a previous post but I will review the pattern here quickly.

First multiple constructors:
  1. scala> class HelloConstructor(param1:Int, param2:Int) {
  2.      | def this(onlyParam:Int) = this(onlyParam,onlyParam)
  3.      | def this(p1:String, p2:String, p3:String) = this(p1.length, p2.length + p3.length)
  4.      | def this(onlyParam:String) = this(onlyParam.length)
  5.      | }
  6. defined class HelloConstructor

In Java if a constructor calls another constructor that call must be the first statement in the constructor. Scala is the same except that in Scala the primary constructor must be called. Notice that all constructors call this(param1,param2) at some point. In addition any method defined in the class HelloConstructor is not available until after the primary constructor is invoked. The following examples are not valid.
  1. scala> class HelloConstructor(param1:Int, param2:Int) {                                  
  2.      | def x = 1
  3.      | def this() = this(x,3)
  4.      | }
  5. <console>:6: error: not found: value x
  6.        def this() = this(x,3)
  7. scala> class HelloConstructor(param1:Int, param2:Int) {
  8.      | def this() = {
  9.      | println("constructing")  // the REPL won't even let me finish method definition
  10. <console>:3: error: 'this' expected but identifier found.
  11.        println("constructing")
  12.        ^

Factory companion objects can be used to work around these restrictions:
  1. scala> class HelloConstructor(param1:Int, param2:Int)  
  2. defined class HelloConstructor
  3. scala> object HelloConstructor {                             
  4.      | def apply() = {
  5.      | println("constructing object")
  6.      | new HelloConstructor(1,2)
  7.      | }
  8.      | }
  9. defined module HelloConstructor
  10. scala> HelloConstructor()
  11. constructing object
  12. res1: HelloConstructor = HelloConstructor@5b0010ec

Since companion objects can access private members of the class the factory methods can be as powerful as a constructor without the restrictions.

One last idea that is useful when designing classes is Scala 2.8 default arguments:
  1. scala> class HelloConstructor(param1: Int = 1, param2: Int = 2)
  2. defined class HelloConstructor
  3. scala> new HelloConstructor()
  4. res0: HelloConstructor = HelloConstructor@7cd47880
  5. scala> new HelloConstructor(1)
  6. res1: HelloConstructor = HelloConstructor@3834a1c8
  7. scala> new HelloConstructor(param1 = 1)
  8. res2: HelloConstructor = HelloConstructor@3b3e3940
  9. scala> new HelloConstructor(param2 = 1)
  10. res3: HelloConstructor = HelloConstructor@6dee2ea8
  11. scala> new HelloConstructor(3,4)       
  12. res4: HelloConstructor = HelloConstructor@397b6074
  13. scala> new HelloConstructor(param1 = 3, param2=4)
  14. res5: HelloConstructor = HelloConstructor@20272fec

No comments:

Post a Comment