| 일 | 월 | 화 | 수 | 목 | 금 | 토 |
|---|---|---|---|---|---|---|
| 1 | 2 | 3 | ||||
| 4 | 5 | 6 | 7 | 8 | 9 | 10 |
| 11 | 12 | 13 | 14 | 15 | 16 | 17 |
| 18 | 19 | 20 | 21 | 22 | 23 | 24 |
| 25 | 26 | 27 | 28 | 29 | 30 | 31 |
- HashMap
- 코딩공부
- 코딩테스트준비
- 멀티스레드
- 개발자팁
- 자바프로그래밍
- 알고리즘공부
- 백준
- 가비지컬렉션
- 예외처리
- 클린코드
- 자바기초
- 개발자취업
- 프로그래밍기초
- 정렬
- 자바공부
- 객체지향
- 코딩테스트
- 코딩테스트팁
- Java
- 메모리관리
- 자바
- 알고리즘
- 개발공부
- 파이썬
- 자바개발
- JVM
- 프로그래머스
- 자료구조
- 코딩인터뷰
- Today
- Total
코드 한 줄의 기록
Java 제네릭 완벽 가이드: 타입 파라미터부터 와일드카드까지 실전 총정리 본문
최근에 자바 프로젝트를 진행하면서 제네릭에 대해 제대로 공부할 필요성을 느꼈습니다. 그동안 ArrayList<String> 정도만 사용했는데, 실무 코드를 보니 <? extends T>나 <? super T> 같은 복잡한 문법이 자주 등장하더군요. 그래서 이번 기회에 제네릭을 처음부터 끝까지 정리하면서 저처럼 제네릭이 헷갈리는 분들과 함께 공부하고자 이 글을 작성하게 되었습니다.
제네릭이 왜 필요한가
자바를 처음 배울 때는 제네릭 없이도 프로그래밍이 가능했습니다. 하지만 실제로 프로젝트를 진행하다 보면 제네릭이 왜 필요한지 절실히 느끼게 됩니다.
List list = new ArrayList();
list.add("Hello");
list.add("World");
String text = (String) list.get(0); // 타입 캐스팅 필요
이런 방식의 문제는 컴파일 시점에는 잘못된 타입을 넣어도 에러가 발생하지 않는다는 것입니다.
List list = new ArrayList();
list.add("a");
list.add("b");
list.add("c");
int a = (Integer) list.get(0) / 1; // 런타임 에러 발생!
반면 제네릭을 사용하면 컴파일 시점에 타입을 체크할 수 있습니다.
제네릭 클래스 만들기
public class Box<T> {
private T content;
public void set(T content) {
this.content = content;
}
public T get() {
return content;
}
}
Box<String> stringBox = new Box<>();
stringBox.set("Hello");
String text = stringBox.get();
Box<Integer> intBox = new Box<>();
intBox.set(100);
Integer number = intBox.get();
제네릭 메서드 제대로 이해하기
public class Util {
public static <T> Box<T> boxing(T t) {
Box<T> box = new Box<>();
box.set(t);
return box;
}
}
// 타입 명시적 지정
Box<Integer> box1 = Util.<Integer>boxing(100);
// 타입 추론
Box<Integer> box2 = Util.boxing(100);
타입 파라미터 제한하기
public class Calculator<T extends Number> {
private T number;
public double getSquare() {
return number.doubleValue() * number.doubleValue();
}
}
와일드카드의 세계
비한정적 와일드카드
public static void printList(List<?> list) {
for (Object item : list) {
System.out.println(item);
}
}
상한 경계 와일드카드
public static double sumOfList(List<? extends Number> list) {
double sum = 0.0;
for (Number number : list) {
sum += number.doubleValue();
}
return sum;
}
하한 경계 와일드카드
public static void addNumbers(List<? super Integer> list) {
list.add(1);
list.add(2);
list.add(3);
}
PECS 공식으로 와일드카드 정복하기
public void printCollection(Collection<? extends MyClass> collection) {
for (MyClass item : collection) {
System.out.println(item);
}
}
public void addElement(Collection<? super MyClass> collection) {
collection.add(new MyClass());
}
제네릭의 타입 소거
// 소거 전
class Box<T extends Number> {
private T value;
T getValue() {
return value;
}
}
// 소거 후
class Box {
private Number value;
Number getValue() {
return value;
}
}
실전에서 자주 마주치는 제네릭 패턴
제네릭 싱글톤 패턴
public class GenericSingleton {
private static final GenericSingleton INSTANCE = new GenericSingleton();
private GenericSingleton() {}
public static <T> Function<T, T> identityFunction() {
return (T t) -> t;
}
}
빌더 패턴과 제네릭
public class Response<T> {
private int code;
private String message;
private T data;
public static <T> Builder<T> builder() {
return new Builder<>();
}
public static class Builder<T> {
private int code;
private String message;
private T data;
public Builder<T> code(int code) {
this.code = code;
return this;
}
public Builder<T> message(String message) {
this.message = message;
return this;
}
public Builder<T> data(T data) {
this.data = data;
return this;
}
public Response<T> build() {
return new Response<>(code, message, data);
}
}
}
제네릭 DAO 패턴
public abstract class BaseDao<T> {
protected Class<T> entityClass;
public BaseDao(Class<T> entityClass) {
this.entityClass = entityClass;
}
public T findById(Long id) { }
public List<T> findAll() { }
public void save(T entity) { }
}
public class UserDao extends BaseDao<User> {
public UserDao() {
super(User.class);
}
}
제네릭 사용 시 주의할 점
// 나쁜 예
List list = new ArrayList();
// 좋은 예
List<Object> list = new ArrayList<>();
@SuppressWarnings("unchecked")
T[] array = (T[]) new Object[10];
제네릭을 공부하면서 처음에는 복잡하고 어려웠지만, 하나씩 정리하다 보니 전체적인 그림이 보이기 시작했습니다. 제네릭 클래스와 메서드의 차이, 와일드카드의 세 가지 형태, PECS 공식 등을 이해하고 나니 실무 코드를 읽는 것도 한결 수월해졌습니다. 앞으로도 코드를 직접 작성해 보면서 익히면 훨씬 자연스럽게 익숙해질 것입니다.
Java Iterator와 Fail-Fast, 컬렉션 수정 시 주의할 점들
자바로 개발하다 보면 리스트나 셋 같은 컬렉션을 순회하면서 요소를 추가하거나 삭제해야 할 때가 있다. 그런데 이 과정에서 ConcurrentModificationException이라는 예외를 만나본 적 있지 않은가? 처
byteandbit.tistory.com
'JAVA' 카테고리의 다른 글
| Java 스트림 API 완벽 정리: 중간연산·최종연산·파이프라인 총정리 (0) | 2025.11.01 |
|---|---|
| Java 함수형 인터페이스와 메서드 레퍼런스 완벽 가이드 (0) | 2025.10.29 |
| Java Iterator와 Fail-Fast, 컬렉션 수정 시 주의할 점들 (0) | 2025.10.27 |
| Java Comparable과 Comparator 완벽 가이드 - 정렬 전략 완전 정복 (0) | 2025.10.27 |
| Java Queue, Deque, PriorityQueue 완벽 가이드 (0) | 2025.10.27 |