Generics

Generics allow you to parameterize types (classes, interfaces, methods).

  • Instead of specifying a type upfront, you defer that decision to when the class or method is used (compile-time, not runtime).
  • Used extensively in collections (e.g., List<E>, Map<K,V>).
  • Cannot be used with primitive types directly → Use wrapper classes (int → Integer, double → Double)
  • Cannot use with class-level variables
    • generics are type parameters bound to instances, and static members belong to the class itself, not any particular instance
    • If you could use generics to class variables, then the type of the class variables would differ by instance, making them not share the same variable
    • Therefore, there’s no type context for the static field/method

Common Type Parameter Names (convention):

  • Although you can use lowercase names or other identifiers, ☕Java convention prefers these uppercase letters for clarity.
  • These are just names, and the actual types are determined during runtime (when they are actually used)
SymbolMeaning
TType
EElement
KKey
VValue
NNumber

Generic Class

  • Classes where generics are being applied
  • <T> is the type parameter
class Basket<T> {
    private T item;
 
    public Basket(T item) {
        this.item = item;
    }
 
    public T getItem() {
        return item;
    }
 
    public void setItem(T item) {
        this.item = item;
    }
}
  • Since generic classes are not tied to a specific type, when instantiating a generic class, you need to specify the intended type like below:
Basket<String>  basket1 = new Basket<String>("Hello");
Basket<Integer> basket2 = new Basket<Integer>(10);
Basket<Double>  basket3 = new Basket<Double>(3.14);
 
Basket<String> basket1 = new Basket<>("Hello"); // ✅ allowed
  • you cannot use primitive types (like int, double, etc.) as type arguments use wrapper classes
  • The compiler can infer the type from the variable declaration.

Generic method

Generic

While you can declare an entire class as generic, you can also declare only specific methods inside the class as generic. These are called generic methods.

  • A generic method defines its type parameter before the return type, and this type parameter is only valid within that method.
    • 내부 구조 (static, heap, class method ) 이해 + 복습!
class Basket<T> {
    private T item;
 
    public Basket(T item) {
        this.item = item;
    }
 
    public T getItem() {
        return item;
    }
 
    public void setItem(T item) {
        this.item = item;
    }
 
	....
}
  • The type parameter of a generic method is completely separate from the class’s generic type parameter — even if they use the same letter.
class Basket<T> {
    public <T> void add(T element) {
        // This T is NOT the same as the class's T
    }
}
  • Class type parameter is decided at instantiation time
  • Method type parameter is decided at method call time
Basket<String> basket = new Basket<>();
basket.<Integer>add(10); // 메서드 내 T = Integer
basket.add(10);          // 타입 추론 가능
  • basket.<Integer>add(10);
    • the generic method public <T> void add(T element) is called with T = Integer
    • “For this call to add, treat the type parameter <T> as Integer.”
    • If the method was just public void add(T element) { ... }, we wouldn’t be able to override the type

Printing stuff in list

public class GenericPrinter {
	public static <T> void printArray(T[] array) {
		for (T elem: array) {
			System.out.println(element);
		}
	}
}
String[] names = {"Alice", "Bob", "Charlie"};
Integer[] numbers = {1, 2, 3};
GenericPrinter.printArray(names);
GenericPrinter.printArray(numbers);

Comparable

public class GenericComparator {
    public static <T extends Comparable<T>> T max(T a, T b) {
        return a.compareTo(b) > 0 ? a : b;
    }
}
 
System.out.println(GenericComparator.max(10, 20));
System.out.println(GenericComparator.max("apple", "banana"));
  • public static <T extends Comparable<T>> T max(T a, T b)
    • <T extends Comparable<T>> : type parameter
      • T is a type that must implement Comparable<T>
      • Only types that can compare themselves to other instances of the same type (T) can be used here.
        • Integer implements Comparable<Integer>
        • Double implements Comparable<Double>
      • T should be something that can use the .compareTo() method
    • T : return type
      • same T declared in the type parameter
    • max(T a, T b)
      • Parameters: two values of type T

Polymorphism

class Flower { ... }
class Rose extends Flower { ... }
class RosePasta { ... }
 
class Basket<T> {
    private T item;
 
    public T getItem() {
        return item;
    }
 
    public void setItem(T item) {
        this.item = item;
    }
}
 
class Main {
    public static void main(String[] args) {
        Basket<Flower> flowerBasket = new Basket<>();
        flowerBasket.setItem(new Rose());      // 다형성 적용
        flowerBasket.setItem(new RosePasta()); // 에러
    }
}

Bounded Type Parameter

  • This helps ensure that only objects with a specific interface or superclass can be used, allowing access to the methods defined in the bound
class Flower { ... }
class Rose extends Flower { ... }
class RosePasta { ... }
 
// HERE!!!
class Basket<T extends Flower> {
    private T item;
    public T getItem() { return item; }
    public void setItem(T item) { this.item = item; }
}
 
class Main {
    public static void main(String[] args) {
        Basket<Rose> roseBasket = new Basket<>();
        // Basket<RosePasta> rosePastaBasket = new Basket<>(); // 에러
    }
}
  • Now Basket only allows subclasses of Flower
  • You use extends keyword on both classes and interfaces
    • extends is used to set upper bounds on type parameters.
    • super is not used in type parameter declarations like <T super X> — it’s only used in wildcard types (e.g., List<? super Integer>).
interface Plant { ... }
class Flower implements Plant { ... }
class Rose extends Flower implements Plant { ... }
 
class Basket<T extends Flower & Plant> {
    private T item;
    public T getItem() { return item; }
    public void setItem(T item) { this.item = item; }
}
  • You use the & operator for both classes and/or interfaces
    • Classes must come first, the rest interfaces (otherwise it’s a compile error)

Wildcard

  • wildcards in generic can turn into ANYTHING
  • ?
class Phone {
	public <? extends Phone> void call(<? extends Phone>);
}
 
class IPhone extends Phone {}
class Galaxy extends Phone {}
 
class IPhone12Pro extends IPhone {}
class IPhoneXS extends IPhone {}
 
class S22 extends Galaxy {}
class ZFlip3 extends Galaxy {}
class Phone {
	public void call(<? extends Phone> phone) {};
}
 
class IPhone extends Phone {}
class Galaxy extends Phone {}
 
class IPhone12Pro extends IPhone {}
class IPhoneXS extends IPhone {}
 
class S22 extends Galaxy {}
class ZFlip3 extends Galaxy {}

number ㄴ Integer ㄴ Double

  • printBox

    • setValue를 쓸때 int인지 double인지 모름
  • addToBox

    • getValue할때 super을 사용했으니까 object밖에 못들어옴
    • object로 값을 받는건 제대로 조회하는게 아님
  • 조회할때는 extends, 추가할때는 super