Java Abstraction
Abstraction means showing what something does and hiding how it does it. In Polymorphism the class Shape had an area() method that returned 0, only so that subclasses could override it. That is awkward: there is no such thing as a plain shape, and a subclass might forget to override area. An abstract class solves both problems.
Abstract classes and methods
A class marked abstract cannot be created with new; it exists only to be extended. An abstract method has no body, just a semicolon. Every non-abstract subclass must provide it, or the program does not compile:
abstract class Shape {
abstract double area();
abstract double perimeter();
String describe() {
return String.format("%s with area %.2f and perimeter %.2f",
getClass().getSimpleName(), area(), perimeter());
}
}
class Rectangle extends Shape {
private final double w, h;
Rectangle(double w, double h) {
this.w = w;
this.h = h;
}
@Override
double area() {
return w * h;
}
@Override
double perimeter() {
return 2 * (w + h);
}
}
class Circle extends Shape {
private final double r;
Circle(double r) {
this.r = r;
}
@Override
double area() {
return Math.PI * r * r;
}
@Override
double perimeter() {
return 2 * Math.PI * r;
}
}
public class Main {
public static void main(String[] args) {
Shape[] shapes = {new Rectangle(3, 4), new Circle(1)};
for (Shape s : shapes) {
System.out.println(s.describe());
}
}
}Rectangle with area 12.00 and perimeter 14.00 Circle with area 3.14 and perimeter 6.28
Shape mixes both kinds of methods: the abstract ones say what every shape must be able to do, and describe is shared code that works for all of them by calling the abstract methods.
You cannot create an abstract object
abstract class Shape {
abstract double area();
}
public class Main {
public static void main(String[] args) {
Shape s = new Shape();
System.out.println(s.area());
}
}Main.java:7: error: Shape is abstract; cannot be instantiated
The compiler refuses, because a Shape with no area would make no sense. A Shape variable is still allowed, and it can hold any subclass object.
Constructors in abstract classes
An abstract class can have fields and constructors. Subclasses call the constructor with super(...), as in Inheritance:
abstract class Employee {
protected final String name;
Employee(String name) {
this.name = name;
}
abstract double monthlyPay();
@Override
public String toString() {
return name + " earns " + monthlyPay();
}
}
class Salaried extends Employee {
private final double salary;
Salaried(String name, double salary) {
super(name);
this.salary = salary;
}
@Override
double monthlyPay() {
return salary / 12;
}
}
class Hourly extends Employee {
private final double rate;
private final int hours;
Hourly(String name, double rate, int hours) {
super(name);
this.rate = rate;
this.hours = hours;
}
@Override
double monthlyPay() {
return rate * hours;
}
}
public class Main {
public static void main(String[] args) {
System.out.println(new Salaried("Neha", 600000));
System.out.println(new Hourly("Omar", 250, 120));
}
}Neha earns 50000.0 Omar earns 30000.0
Abstract classes are best when subclasses share code and fields. When you only want to describe a set of methods, with no shared state, an Interface is usually the better tool.
Practice exercises
- Add a
Trianglesubclass to the first example. What happens if you leave outperimeter()? - Write an abstract class
Vehiclewith an abstract methodwheels()and a normal method that prints it. - Add a third kind of
Employeethat earns a fixed amount plus a bonus.