Abstract classes
Classes you cannot instantiate, methods with no body, and forcing subclasses to fill in the blanks.
Finished reading?
Mark this session so you can track where you are.
Classes you cannot instantiate, methods with no body, and forcing subclasses to fill in the blanks.
Finished reading?
Mark this session so you can track where you are.
You can now write a parent class, extend it, override its methods, and point a parent-typed variable at a child object so the right behavior runs. Here is the awkward gap left over: some parent classes describe a kind of thing that should never exist on its own. What sound does a plain, generic Animal make? There is no honest answer, and yet nothing has stopped you from writing new Animal(). This session closes that gap.
abstract and predict the error from calling new on it.abstract method with no body and say what it forces every subclass to do.Picture the animal hierarchy you have been building. Animal is the parent. Dog, Cat, and Cow extend it, and each one overrides speak() to return its own sound. Polymorphism lets you keep them all in one Animal[] and call speak() on each.
That all works. But there is a quiet problem hiding in the parent. The whole point of Animal is to be a shared shape for real animals. It is not, by itself, a thing you can point at in the world. A real animal is always a dog, or a cat, or some specific kind. So this line is nonsense, even though Java has not yet objected to it:
public class Main { public static void main(String[] args) { Animal a = new Animal(); System.out.println(a.speak()); }} class Animal { String speak() { return "..."; }}...
We were forced to invent a fake sound, "...", just so the parent would compile. That fake body is a smell. It exists only to fill a hole that should never have been askable. The honest statement is: a generic Animal has no sound, because a generic Animal is not a real thing. We want the language to enforce that, so nobody can ever accidentally build one.
Some classes exist purely to be a shared base for others. They describe a category, not a concrete thing. We want to forbid building them directly and remove every fake placeholder body.
The keyword is abstract. You write it in front of class. It means exactly one thing to start with: you cannot create an object of this class with new. The class can still be extended, and its subclasses can still be built. Only the direct new is forbidden.
public class Main { public static void main(String[] args) { Animal a = new Animal(); System.out.println("we never reach this line"); }} abstract class Animal { String name;}Cannot create an object of abstract class 'Animal'
Press Run. The program stops on the new Animal() line with a message saying you cannot create an object of the abstract class Animal. This is the language doing its job: it caught the nonsense for you. Notice the variable type Animal a is still perfectly legal. A variable can be typed as Animal and point at a Dog. What you cannot do is make a bare Animal object to point it at.
abstract changes nothing about how an object behaves at runtime. It is a promise written into the type system: this class is a base, not a destination. It communicates intent to every other programmer (including you, next month) and lets the compiler hold everyone to it.Forbidding new Animal() is half the idea. The other half is more powerful. We can remove the fake speak() body entirely and replace it with a declaration without a body. That is an abstract method: a method that names itself, its return type, and its parameters, then ends with a semicolon instead of a block.
public class Main { public static void main(String[] args) { System.out.println("compiles fine; Animal cannot be built"); }} abstract class Animal { String name; abstract String speak();}compiles fine; Animal cannot be built
Read abstract String speak(); as a sentence: “every animal can speak and returns a Stringwhen it does, but I, the parent, refuse to say what the sound is. That is each subclass's job.” There is no { and no }, because there is no code. The method is a hole with a precise shape, and the shape is the contract.
An abstract method is a promise with no implementation. It declares that every concrete subclass must provide this exact method, and the compiler will not let them forget.
There is a rule that follows automatically, and it is worth saying out loud because it trips people up: a class with even one abstract method must itself be abstract. It has to be. If Animal has an unanswered speak(), then Animal is incomplete, so building one would give you that impossible object again. Java requires the abstract keyword on the class to match the incompleteness inside it. Leave it off and the compiler stops you.
// This does NOT compile. An abstract method forces the class to be abstract. class Animal { abstract String speak(); // abstract method ...} // ... but the class is not marked abstract. Error.error: Animal is not abstract and does not override
abstract method speak() in Animal
class Animal {
^javac actually prints. The fix is one word: write abstract class Animal.Now the satisfying part. A subclass that provides a real body for every abstract method inherited from its parent is called a concrete class. Concrete means complete: no holes left, so you are allowed to build it with new. Here are three concrete animals, each overriding speak(), all driven through one parent-typed array.
public class Main { public static void main(String[] args) { Animal[] zoo = new Animal[3]; zoo[0] = new Dog("Rex"); zoo[1] = new Cat("Mimi"); zoo[2] = new Cow("Daisy"); int i = 0; while (i < zoo.length) { zoo[i].introduce(); i = i + 1; } }} abstract class Animal { String name; abstract String speak(); void introduce() { System.out.println(name + " says " + speak()); }} class Dog extends Animal { Dog(String n) { name = n; } String speak() { return "Woof"; }} class Cat extends Animal { Cat(String n) { name = n; } String speak() { return "Meow"; }} class Cow extends Animal { Cow(String n) { name = n; } String speak() { return "Moo"; }}Rex says Woof Mimi says Meow Daisy says Moo
Step through and watch the loop. The variable zoo[i] is typed Animal, but at runtime each slot holds a real Dog, Cat, or Cow. When introduce() calls speak(), polymorphism picks the override that belongs to the actual object. The parent never knew the sound, and it never needed to. It just trusted that every concrete subclass would supply one, and the abstract method made that trust enforceable.
speak() is abstract, the compiler guarantees that Dog, Cat, and Cow each have it. So inside introduce(), calling speak() is always safe. You get to write code in the parent that depends on behavior the parent itself does not provide.Suppose Animal declared two abstract methods, speak() and move(), and a subclass only filled in speak(). That subclass still has a hole, so it is still incomplete, so it is still abstract, whether you wanted it to be or not. Java will force you to either mark it abstract too, or implement the missing method. You cannot quietly leave a blank and then build the object.
// Machine has TWO abstract methods. Robot fills only ONE, so Robot is still// incomplete. Either implement move(), or mark Robot abstract. This will not compile. abstract class Machine { abstract void start(); abstract void move();} class Robot extends Machine { void start() { System.out.println("booting"); } // move() is missing. Robot still has a hole, so it cannot be concrete.}error: Robot is not abstract and does not override
abstract method move() in MachineHere is the part that makes abstract classes genuinely useful rather than just a way to ban new. An abstract class can hold real, ordinary members alongside its abstract ones. It can have fields, it can have constructors, and it can have fully written concrete methods with normal bodies. So one abstract base class can both share code and force a contract at the same time.
You already saw a hint of this above: introduce() was a concrete method living inside the abstract Animal. Every subclass inherited it for free, without rewriting it. Let us make that pattern the whole point. Below, an abstract Shape declares one abstract method, area(), but provides a concrete describe() that all shapes share.
public class Main { public static void main(String[] args) { Shape[] shapes = new Shape[2]; shapes[0] = new Circle(2.0); shapes[1] = new Rectangle(3.0, 4.0); int i = 0; while (i < shapes.length) { shapes[i].describe(); i = i + 1; } }} abstract class Shape { String name; abstract double area(); void describe() { System.out.println(name + " has area " + area()); }} class Circle extends Shape { double radius; Circle(double r) { radius = r; name = "Circle"; } double area() { return 3.14 * radius * radius; }} class Rectangle extends Shape { double width; double height; Rectangle(double w, double h) { width = w; height = h; name = "Rectangle"; } double area() { return width * height; }}Circle has area 12.56 Rectangle has area 12.0
This prints Circle has area 12.56 then Rectangle has area 12.0. Look at the division of labor. Shape owns the name field and the entire describe() method, written once, shared by both shapes. What Shaperefuses to do is compute an area, because there is no single formula for “a shape”. So area() is abstract, and each subclass brings its own formula. Shared code above, forced contract below, in one class.
It can feel contradictory at first. If you cannot call new on an abstract class, why would it have a constructor? The answer: the constructor is not for building an Animal directly. It runs as part of building a subclass. When you write new Dog(...), Java constructs the Dog, and the parent Animal constructor runs first to set up the part of the object that Animal is responsible for. The subclass reaches it with the super(...) call you learned in method overriding and super.
// An abstract base class with a constructor and shared concrete code. The// constructor runs via super(...) when a concrete subclass is built. abstract class Account { String owner; double balance; Account(String owner, double balance) { // constructor in an abstract class this.owner = owner; this.balance = balance; } abstract void applyInterest(); // each account type decides the rule void summary() { // shared concrete code System.out.println(owner + " holds " + balance); }} class Savings extends Account { Savings(String owner, double balance) { super(owner, balance); // reach the abstract parent's constructor } void applyInterest() { System.out.println(owner + " earns interest"); }} public class Main { public static void main(String[] args) { Account a = new Savings("Aisha", 500.0); a.summary(); a.applyInterest(); }}Aisha holds 500.0 Aisha earns interest
super(...) constructor call between parent and child, so this one is a read-along rather than a Run button. The flow is the honest one Java performs: new Savings(...) calls the Savings constructor, which immediately calls super(owner, balance), running the abstract Account constructor to set up the shared fields. You never built a bare Account, yet its constructor still ran.Keep these two words straight and most of the confusion melts away. Abstract means incomplete on purpose and not buildable. Concrete means complete and buildable.
abstract.new Animal() is an error.new.Abstract classes give you both at once: shared concrete code that subclasses inherit, and a list of blanks that subclasses are forced to fill. Code reuse and an enforced contract, in a single base.
abstract.abstract ReturnType name(params); with a semicolon and no body.new on it.Try each one yourself first, then open the answer.
new Animal() forbidden once Animal is marked abstract, while Animal a = new Dog(); is still allowed?abstract void start(); but will not compile?Shape example. describe() calls area(), yet Shape never gives area() a body. How is that call safe?Vehicle is abstract with two abstract methods, start() and stop(). Class Car extends Vehicle implements only start(). Can you write new Car()? Why or why not?describe() method over copying the same describe() code into Circle and Rectangle separately.Take these away. They continue exactly what we just did.
Shape example into the playground and confirm it prints Circle has area 12.56 and Rectangle has area 12.0. Then add a third concrete subclass, Triangle, with fields base and height and an area() of 0.5 * base * height. Add it to the array and check its line of output.Shape s = new Shape(); to main. Second, in a fresh file, write an abstract void move(); inside a class that is not marked abstract.Employee base for a payroll program. Decide which fields and which one concrete method every employee shares, and which single method should be abstract because different employee kinds are paid differently. Then write two concrete subclasses that fill in that abstract method. You do not need to run it, but the abstract method and the shared concrete method must both be clearly marked.abstract keyword. What does marking a class abstract let the compiler catch that you would otherwise only discover when the program ran?