Dart: Generics

Dart: Generics

In Dart, by default collections are heterogeneous.

However, by the use of generics, we can make a collection to hold homogeneous values.

The use of Generics makes the use of a single compulsory data type to be held inside the collection. Such collections are called type-safe collections. By the use of generics, type safety is ensured in the Dart language.

Collections:

Syntax:

Collection_name <data_type> identifier= new Collection_name<data_type>

Generic List:

In Dart, a List is simply an ordered group of objects. A list is simply an implementation of an array.

main() {  
  List<int> listEx = [];  
  listEx.add(341);  
  listEx.add(1);  
  listEx.add(23);  

  // iterating across list listEx  
  for (int element in listEx) {  
     print(element);  
  }  
}

Output:
341
1
23

Generic Set:

In Dart, a Set represents a collection of objects in which each object can exist only once.

main() {  
  Set <int> SetEx = new Set <int>();  
  SetEx.add(12);  
  SetEx.add(3);  
  SetEx.add(4); 

  // Already added once, hence wont be added
  SetEx.add(3);  

  // iterating across Set SetEx  
  for (int element in SetEx) {  
     print(element);  
  }  
}
Output:
12
3
4

Generic Map:

In Dart, Map is a dynamic collection of the key, value pairs.

main() {  

  // Creating a Map with Name and ids of students
  Map <String,int> mp={'Jinali':1,'Reet':002,'Moksha':003};  
  print('Map :${mp}');  
}

Output:
Map :{Jinali: 1, Reet: 2, Moksha: 3}

Creating Generic Classes

Syntax of Generics classes:

In Dart, generics are defined using angle brackets (<>) to specify a type parameter. For example, a generic class Box<T> can hold values of any type T:

class Box<T> {
  T value;

  Box(this.value);
}

Here, T is a placeholder for the actual type that will be used when creating instances of the Box class. We can create instances of Box with specific types, such as int, String, or custom objects.

// Define a generic class Box
class Box<T> {
  T value;

  Box(this.value);

  void printValue() {
    print('Value: $value');
  }
}

void main() {
  // Create a Box for integers
  var intBox = Box<int>(10);
  intBox.printValue(); // Output: Value: 10

  // Create a Box for strings
  var stringBox = Box<String>('Hello');
  stringBox.printValue(); // Output: Value: Hello
}

In this example:

  • We define a generic class Box<T>, where T is a placeholder for the type of value the box will hold.

  • We create instances of Box with specific types (int and String) by specifying the type in angle brackets (<int> and <String>).

  • We can then use these instances to store and manipulate values of the specified types.

Generics of specified SubType

In Dart, you can specify that a generic type parameter should be a subtype of a certain type using the extends keyword. This allows you to constrain the range of types that can be used with a generic class, function, or method to only those types that are subtypes of the specified type. Here's how you can use generics with a specified subtype:

class Box<T extends num> {
  T value;

  Box(this.value);

  void display() {
    print('Value: $value');
  }
}

void main() {
  var intBox = Box<int>(10);
  intBox.display(); // Output: Value: 10

  var doubleBox = Box<double>(3.14);
  doubleBox.display(); // Output: Value: 3.14

  // This will cause a compilation error because String is not a subtype of num
  // var stringBox = Box<String>('Hello'); 
}

Output:
Value: 10
Value: 3.14

In this example, the generic class Box has a type parameter T that is constrained to be a subtype of num using extends num. This means that T can only be a type that is a subtype of num, such as int or double.

Using extends num ensures that only numeric types can be used with the Box class. If you try to instantiate Box with a type that is not a subtype of num, such as String, you'll get a compilation error.

This way, generics with specified subtypes allow you to enforce constraints on the types that can be used with your generic classes, functions, or methods, providing additional type safety and preventing potential runtime errors.