Intro To Constructors in Dart – Dart OOP Guide

Haris Bin Nasir Avatar

·

·

Constructors are special methods in Dart that are used to create and initialize objects of a class. In this post, we’ll explore the different types of constructors available in Dart, their usage, and how they contribute to making your code more organized and efficient.

For further information on other topics of Dart, click here.

For an in-depth exploration of other OOP topics in Dart, such as inheritance, polymorphism, and encapsulation etc, click here.

What is a Constructor?

A constructor is a method that gets called automatically when an object is created. It is used to initialize the properties of the class. Unlike regular methods, constructors do not have a return type, not even void.

Example of a Simple Constructor
class Car {
  String brand;
  int year;

  // Constructor
  Car(String b, int y) {
    brand = b;
    year = y;
  }

  void displayInfo() {
    print('Brand: $brand, Year: $year');
  }
}

void main() {
  Car car = Car('Toyota', 2021);
  car.displayInfo();  // Output: Brand: Toyota, Year: 2021
}

Default Constructor

A default constructor is a constructor that doesn’t accept any parameters. If you don’t explicitly define a constructor in your class, Dart provides a default constructor automatically.

Example of a Default Constructor
class Bike {
  String brand = 'Unknown';
  int year = 0;

  // Default constructor provided by Dart
  Bike();

  void displayInfo() {
    print('Brand: $brand, Year: $year');
  }
}

void main() {
  Bike bike = Bike();
  bike.displayInfo();  // Output: Brand: Unknown, Year: 0
}

Named Constructors

Dart allows you to create multiple constructors in a class using named constructors. These are especially useful when you want to create different types of objects with the same class.

Example of a Named Constructor
class Student {
  String name;
  int age;

  // Default constructor
  Student(this.name, this.age);

  // Named constructor
  Student.graduate(String name) {
    this.name = name;
    this.age = 22;  // Default age for graduates
  }

  void showInfo() {
    print('Name: $name, Age: $age');
  }
}

void main() {
  Student student1 = Student('Alice', 20);
  student1.showInfo();  // Output: Name: Alice, Age: 20

  Student student2 = Student.graduate('Bob');
  student2.showInfo();  // Output: Name: Bob, Age: 22
}

Factory Constructors

Factory constructors are used when you need to return an instance of the class from the constructor but not necessarily a new instance. This is useful in situations like implementing a singleton pattern or when deciding to return an existing instance instead of creating a new one.

Example of a Factory Constructor
class Dog {
  String name;
  static final Map<String, Dog> _cache = {};

  // Factory constructor
  factory Dog(String name) {
    // Returns an existing instance or creates a new one
    if (_cache.containsKey(name)) {
      return _cache[name]!;
    } else {
      final dog = Dog._internal(name);
      _cache[name] = dog;
      return dog;
    }
  }

  // Private named constructor
  Dog._internal(this.name);

  void bark() {
    print('$name says Woof!');
  }
}

void main() {
  var dog1 = Dog('Buddy');
  var dog2 = Dog('Buddy');

  print(dog1 == dog2);  // Output: true
  dog1.bark();          // Output: Buddy says Woof!
}

Constructor Overloading

Dart doesn’t support traditional constructor overloading (multiple constructors with the same name but different parameters). However, you can achieve similar functionality using named constructors.

Example of Constructor Overloading Using Named Constructors
class Student {
  String name;
  int age;

  // Default constructor
  Student(this.name, this.age);

  // Named constructor
  Student.graduate(String name) {
    this.name = name;
    this.age = 22;  // Default age for graduates
  }

  void showInfo() {
    print('Name: $name, Age: $age');
  }
}

void main() {
  Student student1 = Student('Alice', 20);
  student1.showInfo();  // Output: Name: Alice, Age: 20

  Student student2 = Student.graduate('Bob');
  student2.showInfo();  // Output: Name: Bob, Age: 22
}

Initializer Lists

Initializer lists allow you to assign values to the instance variables before the constructor body executes. This is particularly useful when you need to initialize final fields or when your initialization logic is complex.

Example of an Initializer List
class Point {
  final int x;
  final int y;

  // Constructor with initializer list
  Point(int x, int y) : x = x, y = y;

  void display() {
    print('Point: ($x, $y)');
  }
}

void main() {
  Point p = Point(10, 20);
  p.display();  // Output: Point: (10, 20)
}

Constant Constructors

Constant constructors create compile-time constant objects. These constructors are prefixed with the const keyword. All instance variables in a constant constructor must be final.

Example of a Constant Constructor
class ImmutablePoint {
  final int x;
  final int y;

  // Constant constructor
  const ImmutablePoint(this.x, this.y);
}

void main() {
  const point1 = ImmutablePoint(10, 20);
  const point2 = ImmutablePoint(10, 20);

  print(point1 == point2);  // Output: true
}

Redirecting Constructors

A redirecting constructor calls another constructor in the same class. This is useful when you want to delegate some initialization logic to another constructor.

Example of a Redirecting Constructor
class Circle {
  double radius;

  // Default constructor
  Circle(this.radius);

  // Redirecting constructor
  Circle.diameter(double diameter) : this(diameter / 2);

  double area() {
    return 3.14 * radius * radius;
  }
}

void main() {
  Circle c = Circle.diameter(10);
  print('Area: ${c.area()}');  // Output: Area: 78.5
}

Copy Constructors

Dart doesn’t have built-in support for copy constructors (constructors that create a copy of an object). However, you can manually implement this by creating a constructor that accepts another instance of the class as a parameter.

Example of a Copy Constructor
class Circle {
  double radius;

  // Default constructor
  Circle(this.radius);

  // Redirecting constructor
  Circle.diameter(double diameter) : this(diameter / 2);

  double area() {
    return 3.14 * radius * radius;
  }
}

void main() {
  Circle c = Circle.diameter(10);
  print('Area: ${c.area()}');  // Output: Area: 78.5
}

Conclusion

Constructors in Dart provide flexibility in creating and initializing objects. Whether you’re working with default constructors, named constructors, factory constructors, or constant constructors, understanding how to use them effectively will help you build robust and maintainable Dart applications.

Happy Coding…!!!

Leave a Reply

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