제너릭
✒️ 2025-05-26 18:18 내용 수정
참고 자료 : Inpa dev's 자바 제네릭(Generics) 개념 & 문법 정복하기, Inpa dev's 자바 제네릭의 공변성 & 와일드카드 완벽 이해
클래스나 메서드에서 사용할 내부 데이터 타입을 객체 선언 시 외부에서 미리 지정하여 관리하는 것
- 클래스와 메서드에만 선언할 수 있고, 모든 클래스와 메서드가 사용 가능하다.
- 데이터 타입을 미리 저장해두기 때문에 리턴값에 대한 데이터 타입 변환과 타입 검사 같은 작업을 생략할 수 있다.
- 객체의 타입을 컴파일할 때 체크하기 때문에 객체의 타입에 대해 안정성을 높히고, 형변환을 하는 번거로움을 줄일 수 있다.
- 컴파일할 때 제너릭 타입을 클래스 생성 때 넣었던 타입으로 변환해주므로, 코드 안에 제너릭 타입은 남지 않는다.
제너릭 타입 제한
- 제너릭 클래스나 메서드가 처리할 수 있는 타입을 제한할 수 있다.
- 상위 타입 제한 :
<T extends 상위타입>, T와 그 상위 타입까지 - 하위 타입 제한 :
<T super 하위타입>, T와 그 하위 타입 까지
// Number의 하위 클래스만 올 수 있음
// 본인 ~ Number까지
class ExamList<T extends Number> {}
// Integer와 그의 상위 클래스만 올 수 있음
// Integer ~ 본인까지
class ExamList<T super Integer> {}
1. 제너릭 클래스
클래스 정의 시 멤버의 데이터 타입을 미리 지정하지 않고 인스턴스 생성 시 타입을 지정할 수 있는 클래스
public class 클래스이름<T> {
T data;
}
public interface 인터페이스이름<T> {
T data;
}
| 타입변수 | 의미 |
|---|---|
<T> |
Type * 객체 선언 시에는 T 위치에 Wrapper 클래스로 작성 (Wrapper 클래스) |
<E> |
Element |
<K> |
Key |
<N> |
Number |
<V> |
Value |
- 타입은 두 개 이상의 멀티 타입 파라미터를 사용할 수 있고, 이 경우 각 타입 파라미터를 콤마(
,)로 구분할 수 있다.
class ClassName<T, M> {}
- 인스턴스 생성 시에도 제네릭을 표시한다.
클래스이름<T> 인스턴스이름 = new 클래스이름<T>();
클래스이름<T> 인스턴스이름 = new 클래스이름<>();
// 제너릭 클래스를 생성할 때 타입 변수 자리에 실제 Wrapper 타입 쓰기
MyArray<Integer> myArr = new MyArray<Integer>();
// 클래스 내부 타입들은 정수형으로 변환!
- 클래스와 인터페이스를 동시에 상속받고 구현할 때는 엠퍼센트(
&)를 사용한다.
class MyArray<T> { // T 타입 변수
T element;
void setElement(T element) {
}
T getElement() {
return element;
}
}
// 인터페이스를 구현하는 경우에도 extends
interface Check {}
class ExamList<T extends Check> {}
// 클래스와 인터페이스 동시 상속, 구현한다면 & 사용
class ExamList<T extends Exam & Check> {}
2. 제너릭 메서드
메서드 선언수에 타입 변수를 사용한 메서드
- 메서드가 전달받는 인자 및 메서드 내에서 사용하는 객체가 제너릭 타입을 사용한다면 제너릭 메서드로 사용한다.
- 객체가 멀티 타입 파라미터를 사용한다면
<T, V>로 표현해야 한다.
접근제어자 기타제어자 <T> 리턴타입 메서드이름() {}
- 선언부에서
<T>부분이 매개변수의 제너릭을 표현한 부분이다.- 클래스에서 지정한
T가 아닌 다른 타입을 받을 때만 추가한다.
- 클래스에서 지정한
public <T> T test(T data) {}
- 매개변수의 데이터 타입을 보고 컴파일러가 타입을 추론하기 때문에 호출 시에는 제네릭이 생략될 수 있다.
public static <T> void test() {}
public static <T, V> void test(Object<T,V> obj1, Object<T,V> obj2) {}
ClassName.test(obj1, obj2); // 호출 시 제네릭 생략
3. 와일드카드
- 타입에 제한을 두지 않을 때 사용하며,
?기호로 사용한다.- 참고 자료 : Geeksforgeeks Wildcards in Java
T는 T라는 타입을 명시한 형태이고,?는 타입을 모를 때 사용한다.
<?> //타입 변수에 모든 타입 사용 가능
<? extends T> // 하위 타입이 T까지인 경우만
<? super T> // 상위 타입이 T인 경우만
제너릭 vs 와일드카드
- 제너릭과 와일드카드 모두 컴파일 타임에 타입 검사를 수행하여 런타임 오류를 방지한다.
- 또한 다양한 타입에 대해 동일한 코드 구조를 재사용할 수 있다.
| 항목 | 제네릭 (Generic) | 와일드카드 (Wildcard) |
|---|---|---|
| 정의 위치 | 클래스나 메서드 선언부에서 타입 매개변수로 정의 (<T>) |
타입 사용 시에만 사용 (?, ? extends T, ? super T) |
| 타입 명시 여부 | 명시적으로 타입을 지정하여 사용T는 기본적으로 Object 타입 |
불특정한 타입을 나타내며, 구체적인 타입을 알 필요 없음 |
| 사용 목적 | 특정 타입에 대해 구체적인 동작을 정의할 때 사용 | 다양한 타입을 유연하게 처리할 때 사용 |
| 읽기/쓰기 가능성 | 읽기 및 쓰기 모두 가능 | 제한적? extends T는 읽기 전용? super T는 쓰기 전용 |
| 타입 추론 | 컴파일러가 타입을 정확히 추론 | 컴파일러가 타입을 일부만 추론하거나 제한적 추론 |
| 예시 | <T> void add(T item) |
void add(List<? extends Number> list) |