Published 2014-01-04.
Time to read: 2 minutes.
Auxiliary constructors are implemented differently from Java, and frequently require helper methods in the companion object.
The sample code for this lecture can be found in
courseNotes/
.
Classes always have a primary constructor, which is simply the body of the class, and it can also have auxiliary constructors, which are methods
called this()
.
Auxiliary constructors must call the primary constructor and thereby supply values for each of its parameters that do not have defaults values
defined.
The primary constructor is also referred to as this()
, but with a different method signature.
Let’s modify the class Animal
from a previous lecture and add an auxiliary constructor.
In the following code I defined two factory methods called apply
in the companion object – recall
that apply
is a default method.
Once again we use the REPL’s paste mode in order to define a companion object interactively.
scala> :paste // Entering paste mode (ctrl-D to finish) class Animal3(numLegs: Int, breathesAir: Boolean) { private val breatheMsg = if (breathesAir) "" else " do not" val msg = s"I have $numLegs legs and I$breatheMsg breathe air" def this() = this(numLegs=2, breathesAir=true) // specifying return type is not allowed override def toString = s"numLegs: $numLegs, breathesAir: $breathesAir, msg: $msg" } object Animal3 { def apply(numLegs: Int, breathesAir: Boolean): Animal3 = new Animal3(numLegs, breathesAir) def apply(): Animal3 = new Animal3 // this method definition must have parentheses! } ^D // Exiting paste mode, now interpreting. defined module Animal3 defined class Animal3
Now let’s try out the above definitions.
scala> val animal3a = new Animal3(4, true) animal3a: Animal3 = numLegs: 4, breathesAir: true, msg: I have 4 legs and I breathe air
scala> %}val animal3b = Animal3(4, true) animal3b: Animal3 = numLegs: 4, breathesAir: true, msg: I have 4 legs and I breathe air
scala> %}val animal3c = new Animal3 animal3c: Animal3 = numLegs: 2, breathesAir: true, msg: I have 2 legs and I breathe air
scala> %}val animal3d = Animal3() // without parentheses you get a reference to Animal3.apply(), instead of invoking it animal3d: Animal3 = numLegs: 2, breathesAir: true, msg: I have 2 legs and I breathe air
The rules for when parentheses may or must be applied to method definitions and invocations seem a bit inconsistent. To summarize.
Return type rules
- Constructors and auxiliary constructors may not define return types; the return type is implicit.
- All other methods may define return types. It is good practice to do so.
Parentheses rules when defining methods
- Defining a method with parentheses either suggests that the method has a side effect or it overrides a method that was defined that way.
- Constructors must always be defined with parentheses.
Parentheses rules when invoking methods
- Parentheses are always required if parameters are required when writing with the default infix notation.
- If a constructor does not require parameters, creating an instance of that type with
new
does not need parentheses and they are often not supplied. - Parentheses should be supplied when invoking methods that are defined with parentheses, even if the methods do not require parameters.
- Parentheses must always be supplied when invoking
apply()
. - As we will see in the next course, supplying parentheses can be used to force a function reference to be invoked.
Exercise – Auxillary Constructor
Add an auxiliary constructor to the Bird
you defined in the exercise for the lecture on Scala classes
(src/main/scala/solutions/Bird1.scala
).
Call this new class Bird3
so it does not conflict with the previous Bird
definition.
The auxiliary constructor assumes that the bird can fly, so it only accepts the topSpeed
parameter.
If the Bird3
instance is created at a time with an even minute (e.g.
3:00pm, 3:02pm, 3:04pm...) boost its maximum speed by 10%.
Hint: you can get the current minutes of the hour as a String
with the following incantation.
new java.text.SimpleDateFormat("mm").format(new java.util.Date)
Solution
package solutions class Bird2(val canFly: Boolean, val topSpeed: Double) extends Animal2(2, true) { def this(topSpeed: Double) { this(true, Bird2.newTopSpeed(topSpeed)) } override def toString = s"canFly=$canFly; topSpeed=$topSpeed" } object Bird2 { def newTopSpeed(topSpeed: Double): Double = { val minutesOfHour: String = new java.text.SimpleDateFormat("mm").format(new java.util.Date) val evenMinute: Boolean = minutesOfHour.toInt%2 == 0 if (evenMinute) topSpeed*1.1 else topSpeed } } object Birds2 extends App { val falcon = new Bird2(topSpeed=200) val emu = new Bird2(topSpeed=25) println(s"Falcon $falcon") println(s"Emu $emu") }
This solution is provided as src/main/scala/solutions/Bird2.scala
.
© Copyright 1994-2024 Michael Slinn. All rights reserved.
If you would like to request to use this copyright-protected work in any manner,
please send an email.
This website was made using Jekyll and Mike Slinn’s Jekyll Plugins.