Understanding Generics in Java: Classes, Methods, Interfaces, and Primitive Types


Generics in Java enable developers to create classes, interfaces, and methods with type parameters, providing compile-time type safety and reducing the need for explicit type casting. By using generics, developers can write reusable and flexible code that works with various data types.
This article explores the usage of generics in Java, including their application in classes, methods, and interfaces, handling primitive types via wrapper classes, the concept of autoboxing, and the dangers of using raw types. Additionally, it covers multiple type parameters, upper and lower bounds, multiple bounds using &
, and wildcard types using ?
.
Generic Classes
A generic class defines a type parameter that can be specified when creating an instance of the class. The type parameter allows the class to work with different types without compromising type safety.
Example of a Generic Class
class Box<T> {
private T value;
public void setValue(T value) {
this.value = value;
}
public T getValue() {
return value;
}
}
public class Main {
public static void main(String[] args) {
Box<Integer> intBox = new Box<>();
intBox.setValue(10);
System.out.println("Integer Value: " + intBox.getValue());
Box<String> strBox = new Box<>();
strBox.setValue("Hello Generics");
System.out.println("String Value: " + strBox.getValue());
}
}
Explanation
T
is a type parameter that will be replaced with a concrete type when the class is instantiated.Box<Integer>
ensures type safety, preventing incorrect assignments.Box<String>
provides the same functionality for string values.
Generic Methods
Generic methods allow type parameters to be used within a method independently of the class-level type parameters.
Example of a Generic Method
class Util {
public static <T> void printArray(T[] array) {
for (T element : array) {
System.out.print(element + " ");
}
System.out.println();
}
}
public class Main {
public static void main(String[] args) {
Integer[] intArray = {1, 2, 3, 4};
String[] strArray = {"A", "B", "C"};
Util.printArray(intArray);
Util.printArray(strArray);
}
}
Explanation
<T>
before the return type indicates a generic method.The method
printArray
works with any array type.
Multiple Type Parameters
A generic class can define multiple type parameters, allowing more flexibility.
Example of a Class with Multiple Type Parameters
class Pair<K, V> {
private K key;
private V value;
public Pair(K key, V value) {
this.key = key;
this.value = value;
}
public K getKey() {
return key;
}
public V getValue() {
return value;
}
}
Usage
Pair<String, Integer> student = new Pair<>("Alice", 95);
System.out.println("Student: " + student.getKey() + ", Score: " + student.getValue());
Upper and Lower Bounds
Java generics allow restrictions on the types using extends
(upper bound) and super
(lower bound).
Upper Bounded Wildcard
public static void printNumbers(List<? extends Number> list) {
for (Number num : list) {
System.out.println(num);
}
}
? extends Number
ensures the list contains elements that are instances ofNumber
or its subclasses.
Lower Bounded Wildcard
public static void addNumbers(List<? super Integer> list) {
list.add(10);
list.add(20);
}
? super Integer
ensures the list can containInteger
or its superclasses.
Multiple Bounds
Java allows specifying multiple bounds using &
. The first type must be a class (if any), followed by interfaces.
Example of Multiple Bounds
class Data<T extends Number & Comparable<T>> {
private T value;
public Data(T value) {
this.value = value;
}
public T getValue() {
return value;
}
}
T extends Number & Comparable<T>
ensuresT
is a subclass ofNumber
and implementsComparable
.
Wildcards (?
)
Wildcards (?
) provide flexibility when dealing with generic types.
Unbounded Wildcard
public static void displayList(List<?> list) {
for (Object obj : list) {
System.out.println(obj);
}
}
List<?>
accepts any type of list.
Handling Primitive Types: Wrapper Classes & Autoboxing
Java generics do not support primitive types directly. Instead, they require wrapper classes such as Integer
, Double
, and Character
.
Example of Wrapper Classes in Generics
Box<Integer> intBox = new Box<>(); // Correct
Box<int> intBox = new Box<>(); // Compilation Error
Autoboxing & Unboxing
Autoboxing automatically converts primitive types into their corresponding wrapper classes, and unboxing converts them back.
public class Main {
public static void main(String[] args) {
Box<Integer> intBox = new Box<>();
intBox.setValue(100); // Autoboxing: int -> Integer
int num = intBox.getValue(); // Unboxing: Integer -> int
System.out.println(num);
}
}
Raw Types and Why to Avoid Them
A raw type is a generic class or interface used without specifying a type parameter. This can lead to type safety issues.
Example of Raw Types
Box rawBox = new Box(); // Raw type
rawBox.setValue("String"); // No compile-time check
Integer value = (Integer) rawBox.getValue(); // Runtime error: ClassCastException
Why Avoid Raw Types?
Loss of Type Safety: The compiler does not enforce type checking, leading to potential runtime errors.
Possible
ClassCastException
: Casting might fail if the stored value does not match the expected type.Reduced Code Readability: The intent of the type is not clear when raw types are used.
Conclusion
Generics in Java provide a powerful way to write flexible and type-safe code. By utilizing generic classes, methods, interfaces, multiple type parameters, bounds, and wildcards, developers can build reusable components. Since Java generics do not support primitive types directly, wrapper classes and autoboxing handle the conversion seamlessly. However, using raw types should be avoided due to the risk of runtime errors and loss of type safety.
By understanding and properly implementing generics, Java developers can create robust and maintainable applications.
Subscribe to my newsletter
Read articles from Ali Rıza Şahin directly inside your inbox. Subscribe to the newsletter, and don't miss out.
Written by

Ali Rıza Şahin
Ali Rıza Şahin
Product-oriented Software Engineer with a solid understanding of web programming fundamentals and software development methodologies such as agile and scrum.