OOP(Object-Oriented Programming)
Lecture 7 (Generics)
Generics
• Generics in Java are a powerful feature that allow you to write
flexible, reusable, and type-safe code. They were introduced in
Java 5 to enable types(classes and interfaces) to be parameters
when defining classes, interface, and methods.
• Why Use Generics?
1. Type Safety: Catch type errors at compile time rather than at
runtime.
2. Code Reusability: Write one class or method that works with
different types.
3. Elimination of Type Casting: Avoid explicit casting when
retrieving objects from a collection.
Generics
• Generic Class:
Example of a generic class: Now you can use it like this:
public class Box<T> Box<String> stringBox = new Box<>();
{ private T value; stringBox.set("Hello");
String message = stringBox.get(); // No casting needed
public void set(T value)
{ Box<Integer> intBox = new Box<>();
this.value = value; intBox.set(42);
} Integer number = intBox.get();
public T get()
{
return value;
}
}
Generics
• What does <T> mean?
- It's a type parameter.
- It can be any name (T, E, K, V, etc.), but T is commonly used for
“Type”
- Longer names allowed, but uncommon.
- When using the class, you specify the real type to replace T.
Generics
• Common meanings of type parameter letters:
T – Type.
E – Element (commonly used in collections).
K – Key.
V – Value.
N – Number.
S, U, etc. – Used when you have multiple types.
Generics
• Generic Methods:
Example of a generic method: Now you can use it like this:
public class Utils { Integer[] numbers={1, 2, 3, 4, 5};
public static <T> void printArray(T[] array) { String [] names={“Ali” ,”Sara” , “Ahmed”};
for (T element : array) { Utils.printArray(numbers);
System.out.println(element); Utils.printArray(names);
}
}
}
• <T> is declared before the return type void.
• This method works with any type of array: Integer[], String[],
Double[], etc.
Generics
• Bounded Type:
- Sometimes you want to restrict the generic type to a specific class
or its subclasses:
Example of a bounded type: Now you can use it like this:
public class NumberBox<T extends Number> NumberBox<Integer> intBox = new NumberBox<>();
{ intBox.setNumber(10);
private T number; System.out.println(intBox.getDoubleValue()); // 10.0
NumberBox<String> stringBox = new NumberBox<>();
public void setNumber(T number) // Compile-time error!
{
this.number = number;
}
public double getDoubleValue()
{
return number.doubleValue(); // Because T is a subclass of Number
}
}
Generics
• Wildcards:
Wildcards are used when you want to accept unknown types.
1. <?> — unknown type.
2. <? extends T> — an unknown type that is a subclass of T.
3. <? super T> — an unknown type that is a superclass of T.
Generics
• e.x1:
public void printList(List<?> list) {
for (Object item : list) {
System.out.println(item);
}
}
• e.x2:
public void processNumbers(List<? extends Number> list) {
for (Number n : list) {
System.out.println(n.doubleValue());
}
}
• This allows List<Integer>, List<Double>, List<Float>, etc.
• This used for read-only.
Generics
• e.x3:
public void addIntegers(List<? super Integer> list)
{
list.add(10);
list.add(20);
}
This allows passing:
List<Integer>
List<Number>
List<Object>
But you cannot safely read elements from the list as Integer directly
(only writing is safe).
Generics
• Generics with Collections:
Collections like List, Map, and Set are based on Generics.
e.x:
List<String> names = new ArrayList<>();
names.add("Ali");
names.add("Sara");
String name = names.get(0);
Generics
• Using Multiple Type Parameters:
- You can define more than one generic type in the same class or
method using a comma ,.
Example of a multiple type: Now you can use it like this:
public class Pair<K, V> { Pair<String, Integer> pair = new Pair<>("Age", 30);
private K key; System.out.println(pair.getKey()); // "Age"
private V value; System.out.println(pair.getValue()); // 30
public Pair(K key, V value) {
this.key = key;
this.value = value;
}
public K getKey() { return key; }
public V getValue() { return value; }
}
Generics
• Why can't we use int directly with Generics?
- Because Generics in Java do not accept primitive types such as:
int, double, char, boolean, etc.
- Generics are designed to work only with Objects, so you must
use the object (wrapper) version of each primitive type.
- E.x:
Pair<String, Integer> pair = new Pair<>("Age", 30); // Using Integer instead of int
- If you try to write:
Pair<String, int> pair = new Pair<>("Age", 30); // Compilation error
- You’ll get an error because int is not an object.
Generics
• List of Wrapper Classes for primitive types:
primitive types Wrapper Classes
Int Integer
double Double
float Float
char Character
boolean Boolean
byte Byte
short Short
long Long
Generics
• What types can be used in Type Parameters?
Any reference type (class or interface), such as:
- String, Integer, Double, Boolean, Character, etc.
- Any class you create yourself, e.x:
class Student { ... }
Pair<String, Student> studentPair = new Pair<>("S1", new Student());
- Collections or any type defined in Java or external libraries.
Generics
• Why don't Generics support primitive types?
- Because Generics rely on “Type Erasure”, meaning the type
information is erased at compile-time and treated as Object at
runtime.
- Primitive types do not inherit from Object, and thus cannot be
stored in generic variables that expect objects.
Generics
• How is this handled?
- Java uses a feature called Autoboxing and Unboxing.
• Autoboxing: Automatic conversion from primitive to wrapper
class, e.x:
List<Integer> list = new ArrayList<>();
list.add(5); // int is automatically converted to Integer
• Unboxing: Automatic conversion from wrapper to primitive, e.x:
int x = list.get(0); // Integer is automatically converted to int