Welcome back! Previously, you delved into polymorphism and learned how to create more flexible code structures using classes and inheritance. In this session, we will take a step further and explore a crucial aspect of Object-Oriented Programming: Abstraction.
Abstract classes and abstract methods are essential tools for achieving abstraction. They allow you to define a common interface for a group of derived classes, ensuring that specific methods are implemented. This approach helps you write more robust and scalable programs.
Let's revisit some of the key concepts through the following code example:
Java1// Define an abstract class Shape. Note that the abstractness is achieved by having at least one abstract method. 2abstract class Shape { 3 // Field variables 4 private String color; 5 6 // Constructor to initialize color 7 public Shape(String color) { 8 this.color = color; 9 } 10 11 // Abstract methods for calculating the area and perimeter 12 abstract double area(); 13 abstract double perimeter(); 14 15 // Concrete method to get the color 16 public String getColor() { 17 return color; 18 } 19}
In this snippet, we define an abstract class Shape
with a field variable color
, a constructor to initialize the color, and a concrete method getColor
to retrieve the color. The class also contains two abstract methods: area
and perimeter
. An abstract class can have field variables and fully defined methods, but it must contain at least one abstract method, making it impossible to instantiate directly.
Next, we create concrete classes that extend the abstract class Shape
:
Java1// Define a Circle class that inherits from Shape 2class Circle extends Shape { 3 private double radius; 4 5 // Constructor to initialize the radius and color 6 Circle(double radius, String color) { 7 super(color); 8 this.radius = radius; 9 } 10 11 // Implement the area and perimeter methods 12 @Override 13 double area() { 14 return Math.PI * radius * radius; 15 } 16 17 @Override 18 double perimeter() { 19 return 2 * Math.PI * radius; 20 } 21}
Here, the Circle
class inherits from Shape
and provides concrete implementations for the abstract methods area
and perimeter
. It also includes a constructor to initialize the radius and color by calling the super
constructor from the Shape
class.
Java1// Define a Rectangle class that inherits from Shape 2class Rectangle extends Shape { 3 private double width, height; 4 5 // Constructor to initialize the width, height, and color 6 Rectangle(double width, double height, String color) { 7 super(color); 8 this.width = width; 9 this.height = height; 10 } 11 12 // Implement the area and perimeter methods 13 @Override 14 double area() { 15 return width * height; 16 } 17 18 @Override 19 double perimeter() { 20 return 2 * (width + height); 21 } 22}
Similarly, the Rectangle
class inherits from Shape
and implements the necessary methods area
and perimeter
. It also includes a constructor to initialize the width, height, and color, ensuring that all necessary attributes are properly initialized.
Finally, let's see how we can use these classes in a main method:
Java1public class Main { 2 public static void main(String[] args) { 3 Circle circle = new Circle(5, "Red"); 4 Rectangle rectangle = new Rectangle(4, 6, "Blue"); 5 6 System.out.println("Circle Area: " + circle.area() + ", Perimeter: " + circle.perimeter() + ", Color: " + circle.getColor()); 7 System.out.println("Rectangle Area: " + rectangle.area() + ", Perimeter: " + rectangle.perimeter() + ", Color: " + rectangle.getColor()); 8 9 // Using a Shape reference to a Circle object 10 Shape shape = new Circle(3, "Green"); 11 System.out.println("Shape Area: " + shape.area() + ", Perimeter: " + shape.perimeter() + ", Color: " + shape.getColor()); 12 13 // Uncommenting the following line will cause an error as you can't instantiate an abstract class 14 // Shape invalidShape = new Shape("Yellow"); 15 } 16}
By running the main method, you can see how the Circle
and Rectangle
classes correctly implement the area
and perimeter
methods defined in the Shape
abstract class, while also utilizing the getColor
method. The example also demonstrates polymorphism by using a Shape
reference to a Circle
object.
It's important to note that attempting to instantiate an abstract class directly, as shown by the commented-out line, will result in a compilation error. This reinforces the concept that abstract classes are meant to be subclassed, not instantiated.
Abstract classes and abstract methods offer a way to enforce certain patterns and rules in your code. They allow you to design a system where different types of objects can be treated uniformly while ensuring that specific behaviors are implemented in each derived class.
By mastering abstract classes and abstract methods, you'll be able to:
- Create more organized and readable code: You'll have a clear structure that dictates how certain methods should behave.
- Encourage code reusability: Common code can reside in abstract base classes, reducing redundancy.
- Enhance flexibility: Easily add new types of derived classes without modifying existing code — a key principle of software design.
Intrigued? Let's move on to the practice section and solidify these concepts together. You're on your way to becoming proficient in building sophisticated and maintainable systems!