Generics is a programming concept that allows you to specify the type that you intend to store in a collection. A generic type can be any non-primitive value. The traditional type parameters that you might be familiar with are string and integer.

However, you can create a unique generic type. Furthermore, you can create a generic class and use it to create different collections using generics.

Creating a Generic Type

A generic type is essentially an object, created using a class or an interface. So, for this article, the generic type will be a customer. Using a sample enterprise-level application, the customer represents an entity in a database system. This database system services a furniture company that has three physical locations in three different cities.

 public abstract class Customer {
  private String name;
  public Customer(String name) {
    this.name = name;
  }
  public String getName() {
    return name;
  }
}

You will notice the abstract keyword in the Customer class declaration above. It means you cannot create a direct type or object from the Customer type.

Given that each physical store location will have a unique list of customers, the application will need to have three customer subclasses that you will implement using the inheritance programming concept.

These three subclasses will be the solid types to the generic Customer type. The first Customer subclass will contain the following code:

 public class City1Customer extends Customer{
  public City1Customer(String name) {
    super(name);
  }
}

Creating a Generic Class

To use a generic type, you’ll need to have a generic class or method that accepts this type as a parameter. To create a generic class, you simply need to insert the type parameter into the class declaration.

 public class Promotion<T> {}

The code above indicates that the Promotion class is generic. This means that the Promotion class uses types to create its objects. Now the class declaration can take a collection of objects that are strings, integers, customers, and so on. However, the furniture company only wants the Promotion class to create objects using the Customer types.

 public class Promotion<T extends Customer> {}

The updated code above ensures that the Promotion class only takes the Customer types, this means all subclasses of the Customer class.

Given that there are three stores, the company wants to create one independent promotion for each store. The data required for these promotions are similar. Each promotion will have a name and a collection of customers that won in that promotion.

There are several ways to approach this problem. You could create a Promotion parent class and have three subclasses for each store location. But since each Promotion has a similar structure, you will end up writing duplicate code, which is inefficient (especially since you’re already working with Customer subclasses).

Therefore, a Promotion generic class is one of the more efficient solutions to this problem.

 import java.util.ArrayList;

public class Promotion<T extends Customer> {

  private String promoName;
  private ArrayList<T> winners = new ArrayList<>();

  public Promotion(String promoName) {
    this.promoName = promoName;
  }

  public String getPromoName() {
    return promoName;
  }

  public void addCustomer(T customer) {
    if (winners.contains(customer)) {
      System.out.println( customer.getName() + " is already a winner of this prize.");
    } else {
      winners.add(customer);
      System.out.println( customer.getName() + " is a winner in the " + this.promoName);
    }
  }

  public int numWinners() {
    return this.winners.size();
  }
}

The Promotion generic class has a single variable (promoName). This Java class accepts a collection of customers who won the promotion (winners) and stores them in an ArrayList data structure. To add a new customer to a specific promotion collection, you would need to use the addCustomer() method. If at any point you want to know the number of customers that are in a collection, you can invoke the numWinners() method.

Creating Collections Using a Generic Class

Before you can start creating collections, you’ll need to create customers.

 public class Main {
  public static void main(String[] args) {
    City1Customer john = new City1Customer("John Brown");
    City1Customer kelly = new City1Customer("Kelly James");
    City2Customer jane = new City2Customer("Jane Doe");
    City3Customer jess = new City3Customer("Jess Smith");
  }
}

The Main class above creates four customer objects, each belonging to one of the three store locations. To start creating collections you will need to first create promotion objects (one for each store location).

 Promotion<City1Customer> city1promo = new Promotion<City1Customer>("City1 Promo");
Promotion<City2Customer> city2promo = new Promotion<City2Customer>("City2 Promo");
Promotion<City3Customer> city3promo = new Promotion<City3Customer>("City3 Promo");

Inserting the three lines of code above into the Main class will effectively create three type-specific (or location-specific) promotions. So, only customers from the store in the first city location (City1Customer) can be a winner in that city's promotion (city1promo).

 city1promo.addCustomer(john);
city1promo.addCustomer(kelly);

Therefore, adding the above code to the Main class will produce the following result in the console:

City one winners
Created by writer: Kadeisha Kean

However, if you try to add a third customer to the promotion winners list in the first city, your IDE would produce a compilation error.

Compilation error
Created by writer: Kadeisha Kean

This is because neither Jane nor Jess is a customer of the store in the first city. Creating generic types and classes is useful, as it prevents these simple errors from going uncaught and corrupting a system’s database. In a small application, it can be easy to spot errors like this but in an enterprise-level system, errors like this might go uncaught.

 city2promo.addCustomer(jane);

Replacing the erroneous line of code with the one above will print the following output to the console:

City two winner
Created by writer: Kadeisha Kean

Benefits of Using the Generic Types

From the sample application in this article, you can see that a major advantage of using generic types is compile-time type checking. Without type checking, adding Jane to any of the three promotion lists would not produce an execution-time error, as Jane is indeed a customer of the furniture company.

Generics also facilitates several other advantages including code reusability and scalability. As the company expands to new cities, you can easily create new promotion object types, relying heavily on the existing code. Generic types also allow you to create efficient code, which is a top priority in the software development process.