What an abstract class is
An abstract class is a partly finished class. It can declare methods without a body, and any concrete subclass must supply them. Because it is unfinished, you can't create an object from it directly. Its purpose is to fix a contract for a family of classes while letting each class decide the details.
In this lesson every Animal can speak(), but what it says depends on the kind of animal, so Animal declares the method and leaves the body to its subclasses.
The syntax
abstract class Animal {
def speak(): Unit
}
Two things make this abstract. The class carries the abstract modifier, and speak() has no = and no body. Scala doesn't need an abstract keyword on the method itself: a def without a body is abstract by definition.
Two rules the compiler enforces
You can't instantiate an abstract class. Writing new Animal() fails with Animal is abstract; it cannot be instantiated. Writing Animal() without new fails too, with Not found: Animal, because Scala 3 only offers that shorthand for classes that can be instantiated.
A concrete subclass must implement every abstract member. This subclass leaves speak out:
class Dog extends Animal
The compiler rejects it with class Dog needs to be abstract, since def speak(): Unit in class Animal is not defined. The abstract class is a contract that is checked when the program is compiled.
Mixing abstract and concrete members
An abstract class may also contain ordinary methods and fields. Subclasses inherit those with no extra work:
abstract class Animal {
def speak(): Unit
def describe(): Unit = {
print("I say: ")
speak()
}
}
class Dog extends Animal {
override def speak(): Unit = println("Woof")
}
class Cat extends Animal {
def speak(): Unit = println("Meow")
}
Calling Dog().describe() prints I say: Woof, and Cat().describe() prints I say: Meow. The shared describe lives in one place, and it calls whichever speak the object has.
Notice that Cat leaves out override and still compiles. When you implement an abstract member, override is optional. When you replace a member that already has a body, it is required: if Animal.speak() had a body, Dog without override would fail with method speak of type (): Unit needs `override` modifier. The lesson writes override either way, which is a good habit, since the compiler then catches a misspelled method name.
The exercise, step by step
The starter code gives you the abstract Animal and a Solution class whose mainFunction already creates a Dog and calls speak() on it. That code doesn't compile until Dog exists.
- Declare the subclass. Write
class Dog extends Animalin the empty space belowAnimal. A concrete subclass is the only way to get an object, sinceAnimalitself can't be created. - Implement the method. Inside
Dog, writeoverride def speak(): Unitwith a body that callsprintln("Woof"). The signature must match the abstract one: same name, no parameters, return typeUnit.
The solution in Scala
abstract class Animal {
def speak(): Unit
}
class Dog extends Animal {
override def speak(): Unit = {
println("Woof")
}
}
class Solution {
def mainFunction(): Unit = {
val rex = Dog()
rex.speak()
}
}
Details worth knowing:
Dog()withoutnew. Scala 3 lets you create an instance of a concrete class this way. It is the same asnew Dog().- The type of
rex. It is inferred asDog. Writingval rex: Animal = Dog()would also work and would call the same method, because the object decides whichspeakruns. Unit. The return type for methods that are called for their side effect, here printing.
Common mistakes
- Leaving out
extends Animal. ThenDogis an unrelated class, and definingoverride def speak()in it fails because there is nothing to override. - Changing the signature.
def speak(times: Int): Unitis a different method. The abstractspeak()stays unimplemented, and the compiler asks for it. - Misspelling the name. With
override,def speek()is reported as overriding nothing. Without it, it quietly becomes a new method, and the missing implementation ofspeakis reported instead. - Printing with the wrong text. The test expects exactly
Woof.
Key takeaways
- An abstract class has the
abstractmodifier and may contain methods without bodies. - You can't instantiate it; you extend it with a concrete class that implements every abstract member.
- Concrete members of the abstract class are inherited by all subclasses.
