šŸ”’ Generics and Type Safety

Master Java's type system through visual diagrams, compile-time safety mechanisms, and real-world generic implementations


šŸŽÆ Learning Objectives

By the end of this lesson, you will:


šŸ—ļø Generics Architecture Overview

<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>

Type Safety Evolution

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                                    │
    ā””ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”˜

šŸ­ Generic Classes Mastery

Generic Class Architecture

<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>

šŸŖ Inventory Management System

// šŸŖ 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>
    }
}