제네릭

parkrootseok·2025년 4월 11일

자바

목록 보기
16/19
post-thumbnail

제네릭이란?

제네릭은 데이터 타입을 일반화해서, 컴파일 시점에 타입을 체크할 수 있도록 해주는 문법입니다. 타입 안정성을 확보하고, 형변환 없이 다양한 타입에 재사용할 수 있도록 설계되었습니다.

왜, 제네릭이 등장했을까요?

등장 전

제네릭이 없던 시절에는 컬렉션에 여러 타입을 섞어 담을 수 있었고, 값을 꺼낼 때마다 개발자가 직접 타입 캐스팅을 해야 했습니다. 하지만 잘못된 캐스팅이 발생하면 런타임 에러로 이어지는 문제가 있었습니다.

List list = new ArrayList();

list.add("string");
list.add(100);	// 타입 혼합 가능
String str = (String) list.get(0); // 형변환 필요

등장 후

제네릭 도입 이후에는 컴파일 시점에 타입 검사를 수행하여 잘못된 타입 삽입 자체를 막을 수 있고, 타입 캐스팅 없이 안전하게 값을 사용할 수 있게 되었습니다.

List<String> list = new ArrayList();

list.add("string");
list.add(100); // 컴파일 에러 발생
String str = list.get(0); // 형변환 불필요

와일드카드

와일드카드는 제네릭 타입을 더 유연하게 사용할 수 있도록 도와주는 문법입니다.

<?>

<?>는 모든 타입을 허용함을 의미합니다.

public void print(List<?> list) {
	for (Object obj : list) {
    	System.out.println(obj);
    }
}

<? extends T>

<? extends T>는 T 또는 T의 자식 클래스만 허용함을 의미합니다

public void printAnimals(List<? extends Animal> list) {
	for (Animal animal : list) {
    	System.out.println(animal);
	}
}

// 아래 모두 가능
printAnimals(new ArrayList<Dog>());
printAnimals(new ArrayList<Cat>());
printAnimals(new ArrayList<Animal>());

<? super T>

<? super T>는 T 또는 T의 부모 클래스만 허용함을 의미합니다.

public void saveDogs(List<? super Dog> list) {
    list.add(new Dog());
    list.add(new SubDog());
}

// 아래 모두 가능
saveDogs(new ArrayList<Dog>());
saveDogs(new ArrayList<Animal>());
saveDogs(new ArrayList<Object>());

PECS 원칙이란?

PECS 원칙은 자바 제네릭에서 데이터를 조회(Producer)할 때는 extends, 적재(Consumer)할 때는 super를 사용하는 설계 원칙입니다.

왜, 두 와일드카드를 분리해서 사용할까요?

자바 제네렉의 설계 철학은 타입 안정성입니다. 특히, List와 같이 읽기(get)와 쓰기(add)가 동시에 가능한 자료구조에서는 읽기와 쓰기 목적이 다를 때 발생할 수 있는 타입 문제를 컴파일 단계에서 방지하기 위해 extendssuper를 나눠서 사용합니다.

<? extends T>로만 처리했을 때 문제

// 아래의 경우 Animal의 하위 클래스인 List<Dog>, List<Cat> 등 모두 들어올 수 있습니다.
// 만약, 들어온 리스트가 List<Cat>이라면 아래 코드의 경우 문제가 발생할 수 있습니다.
public void addAnimal(List<? extends Animal> list) {
    list.add(new Dog());
}

<? super T>로만 처리했을 때 문제

// 아래의 경우 Dog의 상위 클래스인 List<Object>, List<Animal> 등 모두 들어올 수 있습니다.
// 만약, List<Animal>이 들어올 경우 아래 코드의 경우 컴파일 에러가 발생할 수 있습니다.
public void printAnimals(List<? super Dog> list) {
    Dog dog = list.get(0);
}

위 예시처럼 컴파일 단계에서 타입 문제를 사전에 방지할 수 있으므로, PECS 설계 원칙에 따라 제네릭을 사용합니다.

profile
동료들의 시간과 노력을 더욱 빛내줄 수 있는 개발자가 되고자 노력합니다.

0개의 댓글