Abstract classes in Dart are a fundamental concept in object-oriented programming that allow you to define a class that cannot be instantiated directly. Instead, abstract classes are meant to be subclassed, and they can contain abstract methods that must be implemented by subclasses. This is useful for defining a common interface for a group of related classes.

Key Concepts

  1. Abstract Class: A class that cannot be instantiated and is intended to be subclassed.
  2. Abstract Method: A method that is declared without an implementation and must be overridden in a subclass.
  3. Subclassing: Creating a new class that inherits from an abstract class and provides implementations for its abstract methods.

Defining an Abstract Class

To define an abstract class in Dart, use the abstract keyword before the class declaration. Abstract methods are declared without a body.

abstract class Animal {
  void makeSound(); // Abstract method
}

In this example, Animal is an abstract class with an abstract method makeSound.

Implementing Abstract Methods

A subclass must provide implementations for all abstract methods of its superclass.

class Dog extends Animal {
  @override
  void makeSound() {
    print('Bark');
  }
}

class Cat extends Animal {
  @override
  void makeSound() {
    print('Meow');
  }
}

Here, Dog and Cat are subclasses of Animal and they provide their own implementations of the makeSound method.

Practical Example

Let's create a more detailed example to illustrate the use of abstract classes and methods.

Step 1: Define the Abstract Class

abstract class Shape {
  double getArea(); // Abstract method
  double getPerimeter(); // Abstract method
}

Step 2: Create Subclasses

class Circle extends Shape {
  double radius;

  Circle(this.radius);

  @override
  double getArea() {
    return 3.14 * radius * radius;
  }

  @override
  double getPerimeter() {
    return 2 * 3.14 * radius;
  }
}

class Rectangle extends Shape {
  double width;
  double height;

  Rectangle(this.width, this.height);

  @override
  double getArea() {
    return width * height;
  }

  @override
  double getPerimeter() {
    return 2 * (width + height);
  }
}

Step 3: Use the Subclasses

void main() {
  Shape circle = Circle(5);
  print('Circle Area: ${circle.getArea()}');
  print('Circle Perimeter: ${circle.getPerimeter()}');

  Shape rectangle = Rectangle(4, 6);
  print('Rectangle Area: ${rectangle.getArea()}');
  print('Rectangle Perimeter: ${rectangle.getPerimeter()}');
}

Practical Exercises

Exercise 1: Define and Implement Abstract Class

  1. Define an abstract class Vehicle with abstract methods startEngine and stopEngine.
  2. Create two subclasses Car and Bike that implement the Vehicle class.
  3. Implement the startEngine and stopEngine methods in both subclasses.

Solution

abstract class Vehicle {
  void startEngine();
  void stopEngine();
}

class Car extends Vehicle {
  @override
  void startEngine() {
    print('Car engine started');
  }

  @override
  void stopEngine() {
    print('Car engine stopped');
  }
}

class Bike extends Vehicle {
  @override
  void startEngine() {
    print('Bike engine started');
  }

  @override
  void stopEngine() {
    print('Bike engine stopped');
  }
}

void main() {
  Vehicle car = Car();
  car.startEngine();
  car.stopEngine();

  Vehicle bike = Bike();
  bike.startEngine();
  bike.stopEngine();
}

Exercise 2: Extend the Shape Example

  1. Add a new subclass Square to the Shape abstract class.
  2. Implement the getArea and getPerimeter methods for the Square class.

Solution

class Square extends Shape {
  double side;

  Square(this.side);

  @override
  double getArea() {
    return side * side;
  }

  @override
  double getPerimeter() {
    return 4 * side;
  }
}

void main() {
  Shape square = Square(4);
  print('Square Area: ${square.getArea()}');
  print('Square Perimeter: ${square.getPerimeter()}');
}

Common Mistakes and Tips

  • Forgetting to Override Abstract Methods: Ensure that all abstract methods in the abstract class are overridden in the subclass.
  • Instantiating Abstract Classes: Remember that you cannot create an instance of an abstract class directly.
  • Using Abstract Classes for Interfaces: Abstract classes are useful for defining interfaces that multiple classes can implement.

Conclusion

Abstract classes in Dart provide a powerful way to define common interfaces and enforce a contract for subclasses. By using abstract classes and methods, you can create a more organized and maintainable codebase. In the next section, we will explore interfaces in Dart, which offer another way to define contracts for classes.

© Copyright 2024. All rights reserved