제네릭 (Java Generics)
1. 제네릭의 등장 배경
- JDK 1.5 이전: 모든 객체를
Object
타입으로 처리해야 했음. - 타입 캐스팅을 직접 해야 했고, 타입 체크가 안 되어 런타임 에러 발생 가능성이 높았음.
2. 제네릭의 정의와 목적
제네릭은 "다양한 타입을 처리할 수 있는 유연한 방법"으로, 타입 안정성과 코드 재사용성을 높이기 위해 도입되었음.
- 타입 캐스팅 생략 가능: 컴파일러가 타입을 자동으로 확인하고 변환.
- 타입 안정성 제공: 잘못된 타입이 들어오는 것을 컴파일 시점에 방지.
3. 제네릭의 기본 사용법
- 다이아몬드 연산자 <T>: 과거에
Object
를 사용하던 자리에 구체적인 타입을 명시.
List<String> strings = new ArrayList<>();
class Box<T> {
private T item;
public T getItem() { return item; }
public void setItem(T item) { this.item = item; }
}
public static <T> T getFirstElement(List<T> list) {
return list.get(0);
}
4. 제네릭 타입 이름의 관습
제네릭 타입 매개변수의 이름은 자유롭게 지정할 수 있지만, 코드 가독성을 위해 의미를 가진 이름을 사용하는 것이 관례입니다. 아래는 일반적으로 사용되는 이름들입니다:
- T: Type (임의의 타입을 나타냄, 일반적으로 사용됨)
- E: Element (컬렉션 요소를 나타낼 때 사용)
- K: Key (맵의 키를 나타낼 때 사용)
- V: Value (맵의 값을 나타낼 때 사용)
- N: Number (숫자 타입을 나타낼 때 사용)
- R: Result (결과 타입을 나타낼 때 사용)
5. 제네릭의 범위 제한
- extends: 제네릭 타입의 범위를 제한.
- 예:
<P extends Pieces>
→ Pieces 클래스를 상속받은 클래스만 사용 가능.
6. 제네릭의 한계와 와일드카드 등장
- 제네릭의 한계: 한 번 타입이 결정되면 변경이 불가능하고, 다른 타입 간 형변환이 불가능.
- 와일드카드 <?>: 제네릭 형변환 문제를 해결하기 위해 등장.
- 사용 예:
List<?> list = new ArrayList<String>(); // 제한 없음
List<? extends Number> nums = new ArrayList<Integer>(); // 상한 제한
List<? super Integer> objs = new ArrayList<Number>(); // 하한 제한
- 종류:
<?>:
제한 없음 (Object
와 비슷).<? extends T>:
상한 제한. T의 하위 타입만 허용.<? super T>:
하한 제한. T의 상위 타입만 허용.- 제한: 와일드카드는 클래스와 메서드 선언에서는 사용 불가. 변수나 매개변수에서만 사용 가능.
7. 제네릭 설계와 사용의 구분
- 제네릭 설계: 제네릭 클래스와 메서드 생성.
class Pair<K, V> {
private K key;
private V value;
public Pair(K key, V value) {
this.key = key;
this.value = value;
}
public K getKey() { return key; }
public V getValue() { return value; }
}
?
를 통해 정의된 제네릭 타입을 유연하게 사용.8. 발표 마무리
제네릭은 다음과 같은 장점과 사용 이유:
- 타입 안정성과 코드 재사용성을 동시에 제공.
- 제네릭 클래스와 메서드를 통해 다양한 타입을 처리.
- 와일드카드
?
를 통해 제네릭 타입의 유연성을 확장.
처음엔 어렵게 느껴질 수 있지만, 익숙해지면 안정적이고 효율적인 코드를 작성하는 데 큰 도움이 됨.
'Java' 카테고리의 다른 글
Stream API (1) | 2025.01.25 |
---|---|
ArrayList와 List (1) | 2025.01.03 |