이 게시물은 2025-2026 성공회대학교 GDGoC Backend 스터디 세션을 위해 작성되었습니다.
Generic은 직역하면 ‘일반적인’ 이라는 뜻으로, 하나의 값이 여러 데이터 타입을 가질 수 있도록 하는 문법이다.
당신은 모든 타입을 보관하고 꺼낼 수 있는 클래스를 만들고 싶다.
제네릭이 없다면 Object 클래스를 사용하게 된다.
Object 클래스는 모든 클래스의 최상위 부모이므로 모든 참조형 타입을 담을 수 있기 때문이다.
하지만 Object 클래스를 사용하면 문제가 생긴다.
class Box {
private Object item;
public void set(Object item) { this.item = item; }
public Object get() { return item; }
}
public class BoxMain {
public static void main(String[] args) {
Box box = new Box();
box.set(123); // int -> Integer 오토박싱
System.out.println(box.get()); // 123
String string = (String) box.get(); // 실행 도중 런타임 오류 발생 - ClassCastException
System.out.println(string);
}
}

get() 메서드의 의 반환 타입이 Object 라서, 컴파일러는 “Object → String 형변환은 문법상 가능하다” 라고 보고 컴파일 에러를 내지 않는다.
하지만 box 안에 실제로 들어있는 것은 업캐스팅된 Integer 타입의 123이다.
Integer 타입은 String 타입과 상속 관계가 아니므로 캐스팅 할 수 없어 런타임에 ClassCastException이 터진다.
이 경우 형변환을 여러번 하기 때문에 타입 안정성이 보장되지 않고, 런타임 오류가 터져서 디버깅도 쉽지 않다.
이런 문제를 개선하고자 제네릭이 도입되었다.
제네릭의 구조를 잘 파악해야 후술할 컬렉션을 잘 이해할 수 있다.

제네릭은 배열의 타입을 지정하듯이 리스트 같은 컬렉션 에서 사용할 데이터 타입을 외부에서 지정하는, 타입을 변수화한 문법이다.
클래스명 우측의 < > 안에 들어가는 알파벳은 메소드가 매개변수를 사용하는 것과 비슷해 타입 매개변수 라고 부른다.
클래스를 만들 때, 클래스명 우측의 < > 안에 타입 매개변수 기호를 지정함으로써 제네릭을 선언할 수 있다.
// 제네릭 클래스를 인스턴스화 해보자
Box<Integer> intBox = new Box<>();
Box<Double> doubleBox = new Box<>();
Box<String> stringBox = new Box<>();
public class BoxMain {
public static void main(String[] args) {
Box<Integer> box = new Box<>();
box.set(123);
System.out.println(box.get()); // 123
String string = box.get(); // box의 타입이 Integer로 결정되어 컴파일 오류 발생, 실행 안 됨
System.out.println(string);
}
}
<>)<>는 다이아몬드 연산자이다.new 연산자 뒤에서 사용된다. 컴파일러가 좌변을 보고 생성할 제네릭 객체의 타입을 추론해서 지정해 준다.