generics-and-wildcards
Generics
Generics in Java provide a way to create classes, methods, and interfaces that can work with different data types
Mostly used for :
class / interface definition
method signature
variables
collections
Here we create a generic container which can store value of any data type
public class GenericContainer<T> {
private T value;
public GenericContainer(T value) {
this.value = value;
}
public T getValue() {
return value;
}
public static void main(String[] args) {
// Create a GenericContainer for an Integer
GenericContainer<Integer> intContainer = new GenericContainer<>(42);
int intValue = intContainer.getValue();
System.out.println("Integer Value: " + intValue);
// Create a GenericContainer for a String
GenericContainer<String> strContainer = new GenericContainer<>("Hello, Generics!");
String strValue = strContainer.getValue();
System.out.println("String Value: " + strValue);
}
}
Popular generic type parameter naming convention
T
Stands for "Type" and is a widely used identifier for a generic type.
E
Typically used for elements in collections (e.g., elements in a List or Set).
K,V
Commonly used for key and value types in key-value pair data structures like maps.
N
Often used to represent a number (e.g., Number in mathematics or numerical operations).
S,U,V
Generic type parameters used when you have more than one type parameter in a class or method.
Wildcard
Only used for :
collections (because they are designed to store multiple elements of various types)
Unbounded wildcard
public static void printList(List<?> list) {
for (Object item : list) {
System.out.print(item + " ");
}
System.out.println();
}
public static void main(String[] args) {
List<Integer> integerList = List.of(1, 2, 3);
List<String> stringList = List.of("Hello", "World");
}
Upper-bounded wildcard
Upper bounded means the wildcard ? which extends Numbers can only be a subtype of Numbers for eg. Numbers / Integer / Double
upper bounded by Number (is a subType , extends Number) , examples:
Number (itself)
Integer
Double
Float
public void method(List <? extends Number> list){ ... }
public static double sumOfList(List<? extends Number> numbers){
double sum = 0;
for (Number num : numbers){
sum += num;
}
return sum;
}
public static void main(String[] args) {
List<Integer> integers = Arrays.asList(1, 2, 3);
List<Double> doubles = Arrays.asList(2.5, 4.8, 6.2);
}
Lower-bounded wildcard
Lower bounded means the wildcard ? which is a super to Integer can only be a supertype of Integer for eg. Integer / Object / Number
lower bounded by Integer (is a superType, super Integer) , examples (Integer's parents):
Object
Number
Integer (itself)
public void method(List <? super Integer> list){ ... }
public static void addNumbers(List<? super Integer> list) {
// This method accepts a list of any type that is a supertype of Integer.
list.add(1);
list.add(2);
list.add(3);
}
public static void main(String[] args) {
List<Object> objectList = new ArrayList<>();
List<Number> numberList = new ArrayList<>();
List<Integer> integerList = new ArrayList<>();
addNumbers(objectList);
addNumbers(numberList);
addNumbers(integerList);
System.out.println("Objects: " + objectList);
System.out.println("Numbers: " + numberList);
System.out.println("Integers: " + integerList);
}
Bounded wildcards illustration

When to use generic or wildcards?
Use generics if you do not care about the type
Use wildcards you do care about the type (i.e. you need to perform an operation that only the T type has then you need to specify the restriction on the type [e.g.
<T extends String>
])
Last updated