Java SE 8 Programmer II Exam Series: Generics

Photo by Marco Secchi on Unsplash


Generics enable types (classes and interfaces) to be parameters when defining classes, interfaces, and methods. It enables you to re-use the same code. In this section, we will cover Generic Classes, Generic Interfaces, Generic Methods, and Bounded Type Parameters.

Type Parameter Naming Conventions

By convention, type parameter names are single, uppercase letters. The most commonly used type parameter names are:

E - Element (used extensively by the Java Collections Framework)
K - Key
N - Number
T - Type
V - Value
S,U,V etc. - 2nd, 3rd, 4th types

Generic Classes

Generic classes are very useful if you need to use different classes as the type parameter. You can create a generic class with a generic data type T. For example, the Food class has a generic type variable declared after the name of the class. It means that you can create Food instances using different data types

public class Food<T> {
private T ingredients;
public T getIngredients(){
return ingredients;
public void setIngredients(T ingredients) {
this.ingredients = ingredients;

Let’s create a food instance with String data type

Food<String> food = new Food<>();
food.setIngredients("protein: 200 gr; carb: 300, fat: 75");

Maybe you changed your mind and decided to create another class that holds the ingredients

class Ingredients {
private String name;
private int calories;
Ingredients(String name, int calories) { = name;
this.calories = calories;

Then, you can create a food instance using the Ingredients data type

Ingredients ingredients = new Ingredients("protein", 200); Food<Ingredients> ingredientsFood = new Food<>(); ingredientsFood.setIngredients(ingredients);

Let’s look at the next example. Why don’t we create a food instance with the Map data type? Don’t worry if you don’t understand the syntax. We will cover the Map interface in the next section

Food<Map<String, Integer>> foodAsMap = new Food<>(); 
Map<String, Integer> foods = new HashMap<String,Integer>(); foods.put("protein", 200);
foods.put("carb", 300);
foods.put("fat", 75);

Generic classes can have two type parameters as well. In this example, we defined a FoodMultipleType generic class that has two parameters. K for a map key, V for a map value

public class FoodMultipleType<K, V> implements FoodPair<K, V>
private K name;
private V calories;
public FoodMultipleType(K name, V value) { = name;
this.calories = value;
@Override public K getName() { return name; }
@Override public V getCalories() { return calories; }

To use this class, we can implement the following:

FoodPair<String, Integer> tuna = new FoodMultipleType<String, Integer>("tuna", 205); 
FoodPair<String, Integer> brownRice = new FoodMultipleType<String, Integer>("brown rice", 214);

Generic Interfaces

An interface can also declare a formal type parameter. For example, the List interface uses a generic type.

public interface List<E> extends Collection<E> { 
int size();
boolean isEmpty();
boolean contains(Object var1);

You can also define your own interface. Let’s create a Diet interface. There are three approaches to implement the Diet interface.

public interface Diet<T> { void printDietList(T t); }

Approach 1: Specify the generic type in the class

The following concrete class says that it deals only with LowCarbHighProtein class

class LowCarbDiet implements Diet<LowCarbHighProtein> {
public void printDietList(LowCarbHighProtein t) {
//do something with t

Approach 2: Create a generic class

The following concrete class allows the caller to specify the type of the generic

class LowCarbAbstractDiet<U> implements Diet<U> {
public void printDietList(U u) { // do something}

Approach 3: Not use generics at all

This is the old way of writing code. It generates a compiler warning about LowCarbDietSimple being a raw type, but it compiles

class LowCarbDietSimple implements Diet { 
public void printDietList(Object o) {}

Generic Methods

Generic methods are methods that introduce their own type parameters. The following generic method compares the two FoodMultipleType objects

public static <K, V> boolean compare(FoodMultipleType<K, V> p1, FoodMultipleType<K, V> p2) {
return p1.getName().equals(p2.getName()) &&

Let’s see how we use the compare method

FoodPair<String, Integer> tuna = new FoodMultipleType<String,Integer>("tuna",205); FoodMultipleType<String, Integer> brownRice = new FoodMultipleType<String,Integer>("brown rice",214); 
Util.<String,Integer>compare((FoodMultipleType<String, Integer>) tuna,brownRice);

Bounded Type Parameters

We sometimes need to restrict the type arguments for the methods. Bounded type parameters serve for that purpose.


The unbounded wildcard type is specified using the wildcard character (?), for example, List<?>. This is called a list of unknown type. This method prints a list of any type.

private static void unbound(List<?> list) { 
for (Object elem : list)
System.out.print(elem + " ");

Lower Bound

A lower bounded wildcard is expressed using List<? super ClassName>, such as List<? super Integer>. This method works on lists of Integer and the supertypes of Integer, such as Integer, Number, and Object

private static void lowerBound(List<? super Integer> list) {
for (int i = 0; i < 10 ; i++) {

Upper Bound

An upper bounded wildcard is specified using List<? extends ClassName>, List<? extends Number>. This method works on lists of Number and the subtypes of Number, such as Integer, Double, and Float.

private static double upperBound(List<? extends Number> list){
double s = 0.0;
for (Number n : list)
s += n.doubleValue();
return s;

Let’s look at how these methods are called. Note that we can pass Integer and Double list to the upperBound method. On the other hand, lowerBound method only accepts Integer and its super classes. The undound method doesn’t have a restriction.

List<Integer> intList = Arrays.<Integer>asList(1, 2, 3);
System.out.println(upperBound(intList)); // Output: 6.0
List<Double> doubleList = Arrays.<Double>asList(4.3, 7.1, 2.4);
System.out.println(upperBound(doubleList)); // Output: 13.799999999999999
lowerBound(intList); // Output: 0 1 2
//lowerBound(doubleList); // compile error
unboud(intList); // Output: 1 2 3
unboud(doubleList); // Output: 4.3 7.1 2.4


In this section, we covered Generic Classes, Generic Interfaces, Generic Methods, and Bounded Type Parameters. You can find the source code on GitHub.

Originally published at on January 15, 2020.

Software Engineer, Oracle Certified Java Programmer,

Love podcasts or audiobooks? Learn on the go with our new app.

Recommended from Medium

Chapter 9 Design Great UIs with Bootstrap’s Grid and Components


How to Access Data from Azure Cloud to Pycharm.

hadoop_jar error in the screenshot

Create your first web application via Flask

Running Machine Learning Model inside Docker Container

Coordinating ECS Tasks in Python

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store
Suleyman Yildirim

Suleyman Yildirim

Software Engineer, Oracle Certified Java Programmer,

More from Medium


Code structure in Java

Need for Abstract Classes and Interfaces in JAVA.

Numbers in Java