What inheritance is
Inheritance models an "is-a" relationship. A puppy is a dog, a car is a vehicle, a manager is an employee. When a child class (also called a subclass) inherits from a parent class (a superclass), it gets the parent's fields and methods without repeating them, and it can add its own. This is how code is reused instead of copied.
What is inherited
- Fields. A
Puppyhas anamealthough onlyDogdeclares it. - Methods. A
Puppycan callbark()although onlyDogdefines it. - The type. Anywhere the code expects a
Dog, aPuppycan be used.
The syntax
A child class names its parent with extends in the class header. In Scala, the same header also passes the arguments for the parent's constructor:
class Dog(val name: String) {
def bark(): Unit = {
println(s"Woof! I am $name")
}
}
class Puppy(name: String) extends Dog(name) {
def whine(): Unit = {
println("whimper")
}
}
The header class Puppy(name: String) extends Dog(name) says three things at once. A Puppy is created from one String. It is a kind of Dog. And that string is handed to Dog's constructor, so the parent's name field gets set. There is no separate constructor body to write and no super(...) call, as in some other languages.
Inside the braces, Puppy adds whine. It doesn't redefine bark, because it is inherited.
Using the child class
val buddy = Puppy("Buddy")
buddy.bark()
buddy.whine()
println(buddy.name)
This prints:
Woof! I am Buddy
whimper
Buddy
The call buddy.bark() runs the version declared in Dog, and buddy.name reads the field that Dog declared.
Details that matter
- The parameter is not repeated as a field. In
class Puppy(name: String) extends Dog(name),nameis only a constructor parameter ofPuppy. Writingval namethere would try to define a second field with the same name as the oneDogalready has, and the compiler rejects it:value name of type String needs `override` modifier. - The parent's arguments are required.
class Puppy(name: String) extends Dog, with no(name), doesn't compile, becauseDogcannot be created without a name. The error ismissing argument for parameter name of constructor Dog. - A child can take more parameters than its parent.
class Tagged(name: String, val tag: Int) extends Dog(name)accepts two values, passes one up, and keeps the second as its own field. - A parent variable can hold a child object.
val d: Dog = buddyis valid andd.bark()works. Butd.whine()doesn't compile, because the typeDoghas nowhine. What a variable's type promises is what you can call on it. - A class extends one class. Scala has single inheritance for classes. Sharing behavior across several types is done with traits, which come later.
The exercise
The starter code gives Dog and a mainFunction that creates a Puppy named "Buddy", calls bark() and then whine(). Write Puppy between Dog and Solution:
class Puppy(name: String) extends Dog(name) {
def whine(): Unit = {
println("whimper")
}
}
The expected output is:
Woof! I am Buddy
whimper
If Puppy is missing, the call Puppy("Buddy") fails to compile. If whine is missing, the compiler reports that whine is not a member of Puppy.
Key takeaways
- Use
extendsin the class header to inherit from a parent class. - Pass the parent's constructor arguments right after the parent's name:
extends Dog(name). - The child gets the parent's members and only adds what is new.
- A child object can be used wherever the parent type is expected.
