Master Java's type system through visual diagrams, compile-time safety mechanisms, and real-world generic implementations
By the end of this lesson, you will:
<aside> š”ļø
Generics Philosophy
Generics are like safety contracts - they ensure type correctness at compile-time, preventing ClassCastException at runtime. Think of them as strongly-typed templates that adapt to different data types while maintaining safety.
</aside>
Java Type Safety Evolution - From Danger to Safety
āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā
š„ PRE-JAVA 5 (Raw Types) š”ļø JAVA 5+ (Generics)
āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā
ā List list = new ArrayList(); ā ā List<String> list = new ā
ā list.add("Hello"); ā ā ArrayList<String>(); ā
ā list.add(42); ā š£ DANGER! ā ā list.add("Hello"); ā
ā list.add(new Date()); ā ā // list.add(42); ā ā Compile ā
ā ā ā // Error! ā
ā String s = (String) list.get(0);ā ā String s = list.get(0); ā
ā ā ā ā ā ā
ā š Runtime Exception! ā ā ā
Type Safe! ā
āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā
RUNTIME DISCOVERY COMPILE-TIME SAFETY
(Too Late!) (Catch Early!)
š Type Checking Timeline
āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā
ā Code Writing ā Compilation ā Runtime ā User Experience ā
ā ā ā ā
ā Raw Types: Raw Types: š„ ClassCastException ā
ā No Safety No Checking User Crash ā
ā ā
ā Generics: Generics: ā
Type Safe Operations ā
ā IDE Helps Compiler Happy Users ā
ā Validates ā
āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā
<aside> š¦
Generic Class Philosophy
Generic classes are like smart containers that remember what type of content they hold. Instead of storing everything as Object and casting later, they maintain type information for safety and convenience.
</aside>
// šŖ Generic inventory system for any product type
public class Inventory<T> {
private List<T> items;
private String inventoryName;
private int maxCapacity;
public Inventory(String inventoryName, int maxCapacity) {
this.inventoryName = inventoryName;
this.maxCapacity = maxCapacity;
this.items = new ArrayList<>();
}
// š„ Add item with type safety
public boolean addItem(T item) {
if (items.size() >= maxCapacity) {
System.out.println("ā Inventory " + inventoryName + " is full!");
return false;
}
items.add(item);
System.out.println("ā
Added " + item + " to " + inventoryName);
return true;
}
// š¤ Remove item with type safety
public boolean removeItem(T item) {
boolean removed = items.remove(item);
if (removed) {
System.out.println("ā
Removed " + item + " from " + inventoryName);
} else {
System.out.println("ā Item " + item + " not found in " + inventoryName);
}
return removed;
}
// š Find items with type-safe operations
public List<T> findItemsMatching(Predicate<T> matcher) {
return [items.stream](<http://items.stream>)()
.filter(matcher)
.collect(Collectors.toList());
}
// š Get inventory statistics
public InventoryStats getStats() {
return new InventoryStats(
items.size(),
maxCapacity,
maxCapacity - items.size(),
(items.size() * 100.0) / maxCapacity
);
}
// š Transfer items to another inventory of same type
public int transferTo(Inventory<T> targetInventory, int count) {
int transferred = 0;
Iterator<T> iterator = items.iterator();
while (iterator.hasNext() && transferred < count) {
T item = [iterator.next](<http://iterator.next>)();
if (targetInventory.addItem(item)) {
iterator.remove();
transferred++;
} else {
break; // Target inventory full
}
}
System.out.println("š Transferred " + transferred + " items from " +
inventoryName + " to " + targetInventory.inventoryName);
return transferred;
}
// š List all items (type-safe iteration)
public void displayInventory() {
System.out.println("š " + inventoryName + " Inventory:");
System.out.println(" Capacity: " + items.size() + "/" + maxCapacity);
System.out.println(" Items:");
for (int i = 0; i < items.size(); i++) {
System.out.println(" " + (i + 1) + ". " + items.get(i));
}
if (items.isEmpty()) {
System.out.println(" š Inventory is empty");
}
}
// Getters for type-safe access
public List<T> getItems() { return new ArrayList<>(items); } // Defensive copy
public int size() { return items.size(); }
public boolean isEmpty() { return items.isEmpty(); }
public String getName() { return inventoryName; }
}
// šļø Product classes for demonstration
class Product {
private String name;
private double price;
private String category;
public Product(String name, double price, String category) {
[this.name](<http://this.name>) = name;
this.price = price;
this.category = category;
}
// Getters
public String getName() { return name; }
public double getPrice() { return price; }
public String getCategory() { return category; }
@Override
public String toString() {
return name + " ($" + price + " - " + category + ")";
}
@Override
public boolean equals(Object obj) {
if (this == obj) return true;
if (obj == null || getClass() != obj.getClass()) return false;
Product product = (Product) obj;
return Objects.equals(name, [product.name](<http://product.name>)) &&
Objects.equals(category, product.category);
}
@Override
public int hashCode() {
return Objects.hash(name, category);
}
}
class Book extends Product {
private String author;
private String isbn;
public Book(String name, double price, String author, String isbn) {
super(name, price, "Books");
[this.author](<http://this.author>) = author;
this.isbn = isbn;
}
public String getAuthor() { return author; }
public String getIsbn() { return isbn; }
@Override
public String toString() {
return getName() + " by " + author + " ($" + getPrice() + ")";
}
}
class Electronics extends Product {
private String brand;
private int warrantyMonths;
public Electronics(String name, double price, String brand, int warrantyMonths) {
super(name, price, "Electronics");
this.brand = brand;
this.warrantyMonths = warrantyMonths;
}
public String getBrand() { return brand; }
public int getWarrantyMonths() { return warrantyMonths; }
@Override
public String toString() {
return getBrand() + " " + getName() + " ($" + getPrice() +
" - " + warrantyMonths + "mo warranty)";
}
}
// š Inventory statistics helper class
class InventoryStats {
private final int currentCount;
private final int maxCapacity;
private final int availableSpace;
private final double utilizationPercent;
public InventoryStats(int currentCount, int maxCapacity,
int availableSpace, double utilizationPercent) {
this.currentCount = currentCount;
this.maxCapacity = maxCapacity;
this.availableSpace = availableSpace;
this.utilizationPercent = utilizationPercent;
}
@Override
public String toString() {
return String.format("Stats: %d/%d items (%.1f%% full, %d spaces available)",
currentCount, maxCapacity, utilizationPercent, availableSpace);
}
// Getters
public int getCurrentCount() { return currentCount; }
public int getMaxCapacity() { return maxCapacity; }
public int getAvailableSpace() { return availableSpace; }
public double getUtilizationPercent() { return utilizationPercent; }
}
// šŖ Demonstration class
public class InventorySystemDemo {
public static void main(String[] args) {
// š Book inventory
Inventory<Book> bookStore = new Inventory<>("Downtown Bookstore", 100);
bookStore.addItem(new Book("Clean Code", 45.99, "Robert Martin", "978-0132350884"));
bookStore.addItem(new Book("Effective Java", 52.99, "Joshua Bloch", "978-0134685991"));
bookStore.addItem(new Book("Java Concurrency", 49.99, "Brian Goetz", "978-0321349606"));
// š» Electronics inventory
Inventory<Electronics> techStore = new Inventory<>("Tech Hub", 50);
techStore.addItem(new Electronics("MacBook Pro", 2499.99, "Apple", 12));
techStore.addItem(new Electronics("ThinkPad", 1299.99, "Lenovo", 36));
techStore.addItem(new Electronics("Surface", 1199.99, "Microsoft", 24));
// š Display both inventories
bookStore.displayInventory();
System.out.println(bookStore.getStats());
System.out.println();
techStore.displayInventory();
System.out.println(techStore.getStats());
// š Type-safe searching
List<Book> javaBooks = bookStore.findItemsMatching(
book -> book.getName().toLowerCase().contains("java"));
System.out.println("\\nš Java-related books:");
javaBooks.forEach(book -> System.out.println(" - " + book));
// ā This would be a compile error - type safety in action!
// bookStore.addItem(new Electronics("iPhone", 999.99, "Apple", 12));
// Type mismatch: Electronics cannot be added to Inventory<Book>
}
}