Declaring Type-Safe Collections

Learn about declaring type-safe collections using generics.

Declaring Type-Safe Collections

In the previous lesson, you learned that Dart allows for storing different types of data in one collection. But a problem could arise if Dart is unable to handle all types of data in a given collection.

In this lesson, you will learn to declare type-safe collections to solve this problem.

To ensure type safety, the angular brackets <> with data type enclosed, are used to declare the collection of the given data type. Type safety ensures that only one type of data can be stored in one collection.

Syntax:

CollectionType <dataType> identifier = CollectionType <dataType>();

Example:

List<int> numbers = List<int>();

Generics are parameterized. They use type variable notations to restrict the type of data.

We can understand this with the help of an example. Assume that you have three classes:

  • Product Class: This class represents a grocery item. It has id, title, and price members to define a product.
//A class for grocery product
class Product {
  final int id;
  final double price;
  final String title;
  Product(this.id, this.price, this.title);

  @override
  String toString() {
    return "Price of ${this.title} is \$${this.price}";
  }
}
  • Inventory Class: This class holds information about the inventory of a product. The amount of type int represents the number of items available for a product.
//A class for the product's inventory
class Inventory {
  final int amount;

  Inventory(this.amount);

  @override
  String toString() {
    return "Inventory amount: $amount";
  }
}
  • Store Class: This class uses a HashMap to keep track of products and their inventories. It also has a method to update the inventory and print all products along with their inventory information. We will use parameterized generics to implement the Store class.

Generics are represented in two ways:

  1. Single Letter Names
  2. Descriptive Words

Single Letter Names

Typically, parameters for generics are represented using single-letter names. Some typically used single letter names are:

  • E: The letter E is used to represent the element type in a collection like List.
  • K: The letter K is used to represent the key type in associative collections like Map.
  • V: The letter V is used to represent the value type in associative collections like Map.
  • R: The letter R is used to represent the return type of a method or function.

You can also use a single letter of your choice. We will use the letter P for Product and the letter Ifor Inventory. Check out the Store class implementation with single-letter name parameters.

The Store accepts two parameters as single letter names: P & I.

//Custom type variables- Single letter
class Store<P, I> {
  final HashMap<P, I> catalog = HashMap<P, I>();

  List<P> get products => catalog.keys.toList();

  void updateInventory(P product, I inventory) {
    catalog[product] = inventory;
  }

  void printProducts() {
    catalog.keys.forEach(
      (product) => print("Product: $product, " + catalog[product].toString()),
    );
  }
}

The main() Method:

In the main() method, we create two instances of Product: milk and bread. These two products are added to the store1 inventory. Then, the printProducts() method prints the product name and number of items in the inventory.

void main() {
  Product milk = Product(1, 5.99, "Milk");
  Product bread = Product(2, 4.50, "Bread");

  //Using single letter names for Generics
  Store<Product, Inventory> store1 = Store<Product, Inventory>();
  store1.updateInventory(milk, Inventory(20));
  store1.updateInventory(bread, Inventory(15));
  store1.printProducts();
}

Output:

Product: Price of Milk is $5.99, Inventory amount: 20
Product: Price of Bread is $4.5, Inventory amount: 15

Try it yourself

Let’s put together the single-letter name generics implementation. Run the code by clicking the ‘Run’ button.

Get hands-on with 1400+ tech skills courses.