Generic parameter and Benefits of Generic class and interface


Obtain class that satisfies generic parameter at runtime

Many unbound generic parameters, like those used in a static method, cannot be recovered at runtime (see Other Threads on Erasure). However there is a common strategy employed for accessing the type satisfying a generic parameter on a class at runtime. This allows for generic code that depends on access to type without having to thread type information through every call.

Background

Generic parameterization on a class can be inspected by creating an anonymous inner class. This class will capture the type information. In general this mechanism is referred to as super type tokens, which are detailed in Neal Gafter's blog post.

Implementations

Three common implementations in Java are:

  • Guava's TypeToken
  • Spring's ParameterizedTypeReference
  • Jackson's TypeReference

Example usage

public class DatabaseService<ENTITY_TYPE> {
    private final DatabaseDao databaseDao = new DatabaseDao();
    private final Class<ENTITY_TYPE> entityClass = (Class<ENTITY_TYPE>) new TypeToken<ENTITY_TYPE>(getClass()){}.getRawType();
 
    public List<ENTITY_TYPE> fetchAllEntities() {
        return databaseDao.getAllOfEntityClass(entityClass);
    }
}
 
public class EmployeeService extends DatabaseService<Employee> {}
 
public class Application {
    public static void main(String[] args) {
        EmployeeService employeeService = new EmployeeService();
        List<Employee> employees = employeeService.fetchAllEntities();
    }
}

Benefits of Generic class and interface

Code that uses generics has many benefits over non-generic code. Below are the main benefits

Stronger type checks at compile time

A Java compiler applies strong type checking to generic code and issues errors if the code violates type safety. Fixing compile-time errors is easier than fixing runtime errors, which can be difficult to find.

Elimination of casts

The following code snippet without generics requires casting:

List list = new ArrayList();
list.add("hello");
String s = (String) list.get(0);

When re-written to use generics, the code does not require casting:

List<String> list = new ArrayList<>();
list.add("hello");
String s = list.get(0); // no cast

Enabling programmers to implement generic algorithms

By using generics, programmers can implement generic algorithms that work on collections of different types, can be customized, and are type safe and easier to read.


Instantiating a generic type

Due to type erasure the following will not work

public <T> void genericMethod() {
    T t = new T(); // Can not instantiate the type T.
}

The type T is erased. Since, at runtime, the JVM does not know what T originally was, it does not know which constructor to call.

Workarounds

  • 1. Passing T's class when calling genericMethod:
  • public <T> void genericMethod(Class<T> cls) {
    	try {
    		T t = cls.newInstance();
    	} catch (InstantiationException | IllegalAccessException e) {
    		System.err.println("Could not instantiate: " + cls.getName());
    	}
    }
     
    genericMethod(String.class);

    Which throws exceptions, since there is no way to know if the passed class has an accessible default constructor.

  • 2. Passing a reference to T's constructor:
  • public <T> void genericMethod(Supplier<T> cons) {
    	T t = cons.get();
    }
     
    genericMethod(String::new);

Basic Programs