What overriding is
A child class inherits its parent's methods, but sometimes one of them doesn't fit. A Puppy is a Dog, yet it says "Yip!" instead of "Woof!". Overriding means defining a method in the child class with the same name and parameters as one in the parent, so the child's version replaces the inherited one for objects of the child class.
Callers don't need to know which kind of dog they hold. They call bark() and the right version runs.
The syntax
Scala requires the override keyword when a method replaces a concrete method of the parent:
class Dog(val name: String) {
def bark(): Unit = {
println(s"Woof! I am $name")
}
}
class Puppy(name: String) extends Dog(name) {
override def bark(): Unit = {
println(s"Yip! I am $name")
}
}
The parent's method needs no special marking: Scala methods can be overridden unless they are declared final. The child's method repeats the name, the parameter list and the return type.
What the compiler checks
The override keyword is a safety net, and Scala enforces it from both sides:
- Forgetting it is an error. A
def bark()inPuppywithoutoverridefails withmethod bark of type (): Unit needs `override` modifier. This stops an accidental replacement of an inherited method. - Using it on nothing is an error. A typo such as
override def bork()fails withmethod bork overrides nothing. The compiler catches the misspelled name. - The signature must match. Changing the return type to
Intwhile the parent returnsUnitis rejected as an incompatible type. finalblocks it. If the parent declaresfinal def sleep(), no child can override it, and trying givescannot override final member.
Note that Scala doesn't use the @Override annotation of other languages. Writing it doesn't count as the keyword, and the compiler still asks for override.
Which version runs
The method that runs depends on the real object, not on the type of the variable:
val buddy = Puppy("Buddy")
buddy.bark()
val d: Dog = buddy
d.bark()
val dogs = List[Dog](Dog("Rex"), buddy)
dogs.foreach(_.bark())
The output is:
Yip! I am Buddy
Yip! I am Buddy
Woof! I am Rex
Yip! I am Buddy
The variable d has type Dog, but the object is a Puppy, so the puppy's bark runs. In the list, each element behaves according to what it is. This is called polymorphism: one call, different behavior.
Calling the parent's version
An override can still use the parent's code through super:
class Puppy(name: String) extends Dog(name) {
override def bark(): Unit = {
super.bark()
println(s"Yip! I am $name")
}
}
Now Puppy("b").bark() prints the dog's line first and then the puppy's: Woof! I am b followed by Yip! I am b. This is useful when the child extends the parent's behavior instead of replacing it.
Fields and parameterless methods can be overridden as well, with the same keyword, for example override def legs: Int = 3 in a child of a class that defines def legs: Int = 4.
The exercise
The starter code has Dog and an empty Puppy. Add the override so that the puppy prints Yip! I am followed by its name:
class Puppy(name: String) extends Dog(name) {
override def bark(): Unit = {
println(s"Yip! I am $name")
}
}
Inside the string, $name inserts the constructor parameter that Puppy passed on to Dog. With the name "Buddy", the output is:
Yip! I am Buddy
Common mistakes
- Leaving out
override. The compiler rejects the method. - Changing the signature. With a different parameter list,
overrideis rejected because the method has a different signature than the overridden declaration. - Expecting the parent's output. Once
Puppyoverridesbark, aPuppynever prints the "Woof!" line unless the override callssuper.bark(). - Trying to override a
finalmethod. It is rejected.
Key takeaways
- Overriding replaces an inherited method in the child class.
- Scala requires
override def, and the compiler checks that something is really overridden. - The real object decides which version runs.
super.method()reaches the parent's version.
