Abstract Class in Dart

Abstract Class in Dart

An abstract class is a class that cannot be instantiated directly. Its primary purpose is to serve as a blueprint for other classes. You define an abstract class using the abstract keyword.

Think of it like a template or a contract. It defines a set of methods and properties that any concrete class (a class that can be instantiated) that extends or implements it must follow.

Key Characteristics of Abstract Classes

  • Cannot be Instantiated: You can’t create an object of an abstract class. For example, var myObject = MyAbstractClass(); would result in an error.
  • Can have Abstract Methods: An abstract method is a method declared in an abstract class without a body. It’s simply a signature, ending with a semicolon. Any concrete class that extends the abstract class must provide an implementation (a body) for all of its abstract methods.
  • Can have Concrete Methods: Unlike interfaces in some other languages, a Dart abstract class can have regular, non-abstract methods with a body. These methods can be shared by all subclasses.
  • Can have Instance and Static Variables: Abstract classes can contain both instance and static variables.
  • Inheritance is Key: A class inherits from an abstract class using the extends keyword. This is the only way to use the functionality defined in the abstract class.

Why Use Abstract Classes?

Abstract classes are powerful tools for promoting code reusability and establishing a clear hierarchy within your application. They are especially useful in the following scenarios:

  1. Shared Behavior and Common Blueprint: When you have a group of related classes that share some common functionality but also have unique behaviors. For example, a Shape abstract class could define a common method like calculateArea(), but each subclass (Circle, Square, Triangle) would implement this method differently.
  2. Enforcing a Contract: They force subclasses to implement certain methods, ensuring that a specific set of functionalities is always present in any class that inherits from the abstract class. This makes your code more predictable and easier to maintain.
  3. Preventing Direct Instantiation: They prevent developers from creating a generic, incomplete object. For instance, creating an object of a Vehicle abstract class doesn’t make sense, but creating a Car or Motorcycle object does.

Abstract Class vs. Interface

While Dart doesn’t have a dedicated interface keyword, any class can be an interface. When a class implements another class, it must provide a complete implementation for all of its public methods and properties. The key difference lies in the relationship:

  • Extends (Abstract Class): A class can only extend one other class, inheriting both the implementation and the contract.
  • Implements (Interface): A class can implement multiple classes, but it only inherits the contract (the method signatures) and must provide its own implementation for every method.

Abstract classes are generally preferred when you want to provide a default implementation that subclasses can inherit and optionally override. Interfaces are better when you simply want to define a contract without any shared implementation.


Example: A Vehicle Abstract Class

Abstract Class in Dart

Let’s consider a scenario where we’re building a system for a fleet of vehicles. All vehicles have some common characteristics, but they also have unique behaviors. This is a perfect use case for an abstract class.

Dart

// Define the abstract class
abstract class Vehicle {
  // Abstract method - subclasses MUST implement this
  void accelerate();

  // A concrete method with a default implementation
  void brake() {
    print('Applying brakes.');
  }

  // An abstract getter for a property
  int get numberOfWheels;
}

// Concrete class extending the abstract class
class Car extends Vehicle {
  // Implementing the abstract accelerate method
  @override
  void accelerate() {
    print('The car is accelerating.');
  }

  // Implementing the abstract getter
  @override
  int get numberOfWheels => 4;
}

// Another concrete class
class Bicycle extends Vehicle {
  // Implementing the abstract accelerate method
  @override
  void accelerate() {
    print('The bicycle is pedaling faster.');
  }

  // We can override the concrete brake method if needed
  @override
  void brake() {
    print('Squeezing bicycle brake levers.');
  }

  // Implementing the abstract getter
  @override
  int get numberOfWheels => 2;
}

void main() {
  // You cannot instantiate an abstract class directly
  // var myVehicle = Vehicle(); // This will cause an error

  var myCar = Car();
  myCar.accelerate(); // The car is accelerating.
  myCar.brake();      // Applying brakes. (Uses the inherited implementation)
  print('A car has ${myCar.numberOfWheels} wheels.'); // A car has 4 wheels.

  print('-------------------');

  var myBicycle = Bicycle();
  myBicycle.accelerate(); // The bicycle is pedaling faster.
  myBicycle.brake();      // Squeezing bicycle brake levers. (Uses the overridden implementation)
  print('A bicycle has ${myBicycle.numberOfWheels} wheels.'); // A bicycle has 2 wheels.
}

Explanation of the Example

Abstract Class in Dart
  • We create an abstract class Vehicle.
  • Inside Vehicle, we declare an abstract method accelerate(). Notice it has no body. This forces any subclass to provide its own version of this method.
  • We also include a concrete method brake() with a default implementation. Subclasses can use this as-is or override it.
  • We define a concrete class Car that extends Vehicle. It must provide an implementation for accelerate().
  • We define another concrete class Bicycle, which also extends Vehicle. It provides its own implementation for accelerate() and also overrides the default brake() implementation.
  • In the main function, we demonstrate that we can create objects of Car and Bicycle but not of the Vehicle abstract class itself. We also show how the accelerate() method behaves differently for each subclass, while the brake() method shows how a default implementation can be inherited or overridden.

Leave a Reply

Your email address will not be published. Required fields are marked *