Skip to main content

Command Palette

Search for a command to run...

Generics in Java - Explained !

Generic Programming in Java briefed in simple terms.

Updated
•4 min read•View as Markdown
Generics in Java - Explained !
A

Product-first engineer, blogger and open-source contributor with around 4 years of experience in software development, cloud-native architecture and distributed systems.

I build fintech products that process millions of transactions daily and drive substantial revenue. My expertise spans designing, architecting and deploying scalable software, focusing on the business under the code.

I collaborate closely with engineers, product owners, and guilds, known for my clear communication and team-centric approach in dynamic environments. Colleagues appreciate my adaptability, openness and focus on diverse, meaningful contributions. Beyond coding, I’m recognized for my documentation, ownership and presentation skills, which drive clarity and engagement across teams. Bilingual in English and Deustch, I bridge cross-functional teams across geographies, ensuring smooth, efficient communication.

I’m always open to new opportunities for connection and collaboration. Let’s connect and explore ways to create together.

Generic programming enables developers to create flexible, reusable functions that can work with any valid data type. This is achieved by adjusting our existing code in a way that still maintains type safety. In essence, generics allow us to define classes, interfaces, and methods where types themselves can be parameters. Unlike traditional parameters that take values, type parameters focus on types.

Scope of This Article

In this article, we’ll explore the concept of Generics in Java, addressing both the "Why" and "How" behind generic programming. Our goal is to provide you with a solid understanding of generics without diving too deeply into the underlying mechanisms.

Contents

  • Definition

  • Why Use Java Generics?

  • How to Use Java Generics

  • Bounded Type Parameters

  • Generics and Wildcards

  • Summary

Definition

Generics allow developers to write code that works with different object types—think Integers, Strings, and more—while promoting code reuse and compile-time type safety. This was introduced in Java 5 alongside the Collection Framework. By defining classes, interfaces, and functions that accept types as parameters, we can make our code more reliable. We typically use angle brackets (<>) to denote type parameters, which are often represented by single uppercase letters for clarity.

Example:

Using Java Generics, you can write a generic method to sort a list of arrays, whether they're Integer or String arrays, thus eliminating the need for typecasting.

private List<E> array = new ArrayList<>(size);

Why Use Java Generics?

Before Java 5, it was possible to write code that compiled without error, even if it contained type mismatches:

List arrList = new ArrayList();
arrList.add("Hi, Scaler!");
arrList.add(new Object());

In this scenario, you could add any type of value to the list without specifying the data type. However, when retrieving values, explicit casting was necessary. For instance, the following code could lead to a ClassCastException:

for (int i = 0; i < arrList.size(); i++) {
    String value = (String) arrList.get(i); // ClassCastException when i=1
}

This flexibility, while seeming convenient, could lead to runtime errors. Enter Generics: they were designed to prevent such issues.

With Generics, you can explicitly declare the data type when creating a Java Collection, like so:

List<E> arrayList = new ArrayList<>();

Here, E represents a generic data type parameter. Now, you can ensure that only compatible types—like Integer or String—are stored in arrayList, catching potential type mismatches at compile-time.

How to Use Java Generics

Generics are commonly applied to methods and classes.

  1. Generic Method:
    A generic method is similar to a standard function, but it includes type parameters. This allows for broader usage of the generic approach.

     public static <T> T element(T obj) {
         return obj; // Simply return the object
     }
    
  2. Generic Class:
    Creating a generic class resembles defining a non-generic class, but with a section for type parameters.

     class Test<T> {
         private T item;
         public T getItem() { return item; }
         public void setItem(T item) { this.item = item; }
     }
    

Bounded Type Parameters

Until now, we've seen that any non-primitive type can be used with generics. But what if we want to limit the types that can be used? This is where bounded type parameters come into play.

By declaring a type parameter as a subclass of another type, we can restrict the data types that can be accepted.

For example:

// Accepts only subclasses of List
public class UpperBoundedClass {
    public void upperBoundedMethod(List<?>[] arr) { }
}

In this case, only subclasses of the List type can parameterize UpperBoundedClass and upperBoundedMethod. If an incompatible type is used, the compiler will flag it as an error.

Java Generics Wildcards

Wildcards enable us to pass parameters of generic types to methods. Unlike type parameters, wildcards are represented by the ? symbol.

public void printItems(List<?> list) {
    for (int i = 0; i < list.size(); i++) {
        System.out.println(list.get(i));
    }
}

Summary

Generics in Java function similarly to templates in languages like C++. They are a crucial aspect of the language, enhancing code development by minimising errors. Generics provide compile-time type safety and allow for the implementation of generic algorithms without imposing additional overhead.

Many programming languages, including Java, Python, Go, TypeScript support Generics.

As you explore generics, you'll notice how they simplify code by generalizing classes and methods, reducing redundancy for multiple data types.

Understanding Generics is essential for your growth as a developer. Try applying these concepts in your code, and see how they can streamline your programming practices.

That's all for now. Until next time! 💌

S

Nicely said. I would also add a paragraph about erasure. Essentially, that first code without generics would still compile today and produce some warnings, but work as expected.

Technically, generic code compiles to something pretty similar. The generic data is erased so code like this is legal:

private void myMethod(List<MyObject> data) {
     List myList = (List)data;
     //...
}
1
A

I'm glad that you liked. Erasure - Yeah, I missed it. My bad. I should've included that as well.

Thanks ❤☺

More from this blog

A

Ashwin's Personal Blog

20 posts

Sharing my learnings and experience to the tech community.