Java의 클래스, 메서드, 인터페이스에서 사용할 데이터 타입을 일반화하여 유연성과 코드 재사용성을 높이기 위한 기능
타입을 미리 지정하지 않고 나중에 사용할 때 타입을 명시적으로 정의하는 방법
사용하는 이유
1. 타입 안정성 보장
- 잘못된 타입을 사용하는 실수를 컴파일 시점에서 방지
- 예 : String만 들어가야 할 리스트에 Integer가 들어가지 않도록 방지
2. 형변환의 간소화
- 제너릭을 사용하면 데이터 타입을 미리 명시하므로 명시적 형변환이 필요없음
- 코드가 간결하고 가독성이 높아짐
3. 코드 재사용성
- 다양한 데이터 타입에 대해 동일한 로직을 처리하는 코드를 작성 가능
제너릭 기본 문법
제너릭 클래스 :
// T는 타입 파라미터로, 나중에 실제 데이터 타입으로 대체됨
public class Box<T> {
private T item;
public void setItem(T item) {
this.item = item;
}
public T getItem() {
return item;
}
}
// 사용 예시
Box<String> stringBox = new Box<>();
stringBox.setItem("Hello");
System.out.println(stringBox.getItem()); // 출력: Hello
Box<Integer> integerBox = new Box<>();
integerBox.setItem(123);
System.out.println(integerBox.getItem()); // 출력: 123
제너릭 메서드 :
public class GenericMethodExample {
// 제너릭 메서드 정의 (타입 파라미터 T)
public static <T> void printArray(T[] array) {
for (T element : array) {
System.out.println(element);
}
}
public static void main(String[] args) {
String[] stringArray = {"A", "B", "C"};
Integer[] intArray = {1, 2, 3};
printArray(stringArray); // 출력: A B C
printArray(intArray); // 출력: 1 2 3
}
}
제너릭 인터페이스 :
public interface GenericInterface<T> {
void perform(T item);
}
// 구현 클래스
class StringProcessor implements GenericInterface<String> {
@Override
public void perform(String item) {
System.out.println("Processing: " + item);
}
}
// 사용
GenericInterface<String> processor = new StringProcessor();
processor.perform("Hello"); // 출력: Processing: Hello
타입 파라미터
타입 파라미터 | 의미 |
T | Type (일반적인 타입) |
E | Element (컬렉션 요소) |
K | Key (맵의 키) |
V | Value (맵의 값) |
N | Number (숫자 타입) |
제너릭에서 와일드카드(?)
와일드카드는 특정 타입이 아닌 모든 타입을 나타내기 위해 사용
종류
1. 제한없는 와일드카드(?)
- 어떤 타입도 받을 수 있음
public void printBox(Box<?> box) {
System.out.println(box.getItem());
}
2. 상한 제한 와일드카드(<? extends Type>)
- 특정 클래스나 그 하위 클래스를 제한
public void printNumbers(List<? extends Number> list) {
for (Number number : list) {
System.out.println(number);
}
}
3. 하한 제한 와일드카드(<? super Type>)
- 특정 클래스나 그 상위 클래스를 제한
public void addElements(List<? super Integer> list) {
list.add(10); // Integer 또는 그 하위 타입 추가 가능
}
한계
1. 기본타입 사용 불가
- 제너릭은 기본타입(int, double 등)을 사용할 수 없으며, 대신 래퍼클래스(Integer, Double)를 사용해야함
List<int> list; // 오류
List<Integer> list = new ArrayList<>(); // 가능
2. 런타임 소거(Type Erasure)
- 컴파일 후 제너릭 정보는 제거되므로 런타임에서는 원시타입(Raw Type)처럼 작동
- 따라서 런타임에는 제너릭 타입 정보를 알 수 없음
if (list instanceof List<String>) { // 오류
// 실행 불가: 제너릭 타입은 런타임에 소거됨
}
3. 배열 생성 불가
T[] array = new T[10]; // 오류
요약
1. 제너릭(Generic)은 타입을 일반화하여 유연성을 높이고 코드의 재사용성을 강화
2. 장점:
- 타입 안전성 보장
- 불필요한 형변환 제거
- 다양한 타입에 대해 동일한 로직 처리 가능
3. 주요 용법:
- 제너릭 클래스, 제너릭 메서드, 제너릭 인터페이스
- 와일드카드(?, <? extends>, <? super>)
4. 활용:
- 컬렉션 프레임워크, 스프링의 JPA, 사용자 정의 클래스 등에 광범위하게 사용
출처 : ChatGPT
'BE > Java' 카테고리의 다른 글
[Java] Jackson 라이브러리 (0) | 2025.01.07 |
---|---|
[JPA] N + 1 문제 (0) | 2025.01.06 |
[Java] enum (1) | 2025.01.01 |
[Java] @IdClass (0) | 2024.12.31 |
[Java] @Builder, @ToString, @EqualsAndHashCode, @Singular (0) | 2024.12.23 |