Java의 제네릭

곰태우·2025년 9월 18일

Java

목록 보기
2/3
post-thumbnail

출처 : 블로그 '너지실개발자'

이 게시물은 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이 터진다.

이 경우 형변환을 여러번 하기 때문에 타입 안정성이 보장되지 않고, 런타임 오류가 터져서 디버깅도 쉽지 않다.

이런 문제를 개선하고자 제네릭이 도입되었다.

제네릭의 구조

제네릭의 구조를 잘 파악해야 후술할 컬렉션을 잘 이해할 수 있다.

출처 : 블로그 'Inpa Dev'

제네릭은 배열의 타입을 지정하듯이 리스트 같은 컬렉션 에서 사용할 데이터 타입을 외부에서 지정하는, 타입을 변수화한 문법이다.

제네릭 선언

클래스명 우측의 < > 안에 들어가는 알파벳은 메소드가 매개변수를 사용하는 것과 비슷해 타입 매개변수 라고 부른다.
클래스를 만들 때, 클래스명 우측의 < > 안에 타입 매개변수 기호를 지정함으로써 제네릭을 선언할 수 있다.

  • 매개변수: 메서드가 실행될 때 외부에서 값을 전달받아 사용하는 변수

제네릭 사용 예시

// 제네릭 클래스를 인스턴스화 해보자
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 연산자 뒤에서 사용된다. 컴파일러가 좌변을 보고 생성할 제네릭 객체의 타입을 추론해서 지정해 준다.

제네릭 사용시 장점

  1. 잘못된 타입이 들어올 가능성을 컴파일 단계에서 방지할 수 있다. 즉, 타입 안정성이 확보된다.
  2. 타입별로 같은 클래스를 여러번 만들 필요가 없어 코드의 재사용성이 높아진다.
profile
막상 해보면 별거 아니다

0개의 댓글