🔤 제네릭이란?
- 객체를 생성할 때 실제 사용할 타입을 지정할 수 있도록 하는 기능
- 즉, 여러 타입에 대해 재사용 가능한 코드를 작성할 수 있게 도와주는 문법
✅ 제네릭을 사용하는 이유
1. 타입 안정성(type safety) 확보
List<String>에 Integer를 넣으면 컴파일 오류 발생2. 형변환(casting) 없이 사용 가능
3. 코드 재사용성 증가
⚠️ 제네릭이 없는 경우
❗재사용 불가
- 특정 타입으로 고정되어 있어 재사용이 어려움
- 다시 사용하려면 다른 클래스를 만들어야 함 → 낮은 유연성
public class Box {
private Integer item; // ⚠️ Integer 타입으로 고정
public Box(Integer item) { // ⚠️ Integer 타입으로 고정
this.item = item;
}
public Integer getItem() {
return this.item;
}
}
public class Main {
public static void main(String[] args) {
// ✅ Integer 타입 박스
Box box1 = new Box(100);
// ❌ String 타입을 저장하려면 새로운 클래스를 만들어야 함
Box box2 = new Box("ABC");
}
}
❗타입 안정성 보장 불가
- 사용 시 형 변환이 발생하고 실수로 잘못된 타입을 사용하면 런타임 오류 발생
- 잘못된 다운캐스팅 활용 시 →
ClassCastException
public class ObjectBox {
private Object item; // ⚠️ 다형성: 모든 타입 저장 가능 (하지만 안전하지 않음)
public ObjectBox(Object item) {
this.item = item;
}
public Object getItem() {
return this.item;
}
}
public class Main {
public static void main(String[] args) {
// ✅ ObjectBox 사용
ObjectBox objBox = new ObjectBox("Hello");
String str = (String) objBox.getItem(); // 형변환 필요
System.out.println("objBox 내용: " + str); // Hello
// ⚠️ 실행 중 오류 발생 (잘못된 다운 캐스팅: ClassCastException)
objBox = new ObjectBox(100); // 정수 저장
String error = (String) objBox.getItem(); // ❌ 오류: Integer -> String
System.out.println("잘못된 변환: " + error);
}
}
🧩 <T> 타입 매개변수
✅ 제네릭 클래스에서의 예시
public class Box<T> {
private T item;
public void set(T item) {
this.item = item;
}
public T get() {
return item;
}
}
T는 Box 클래스가 어떤 타입을 담을지 정하고 있다가 객체를 만들 때 결정하게 함Box<String> stringBox = new Box<>();
stringBox.set("안녕");
String str = stringBox.get();
Box<String>을 쓰면 컴파일러가 T를 String으로 치환해서 생각⚡️타입 소거(Erasure)란?
- 컴파일 시점에 제네릭 타입 정보를 제거하는 과정
<T>타입 매개변수 부분은Object로 대체됨- 필요한 경우 컴파일러가 자동으로 강제 다운캐스팅(cat) 코드를 삽입하여 타입 안정성을 보장
⚙️ 제네릭 메서드(Generic Method)란?
- 메서드 내부에서 사용할 타입을 유연하게 지정하는 기능
- 클래스 제네릭 타입과 별개로 독립적인 타입 매개변수를 가짐
🗒️ 예시 코드:
public class GenericBox<T> {
// 속성
private T item;
// 생성자
public GenericBox(T item) {
this.item = item;
}
// 기능
public T getItem() {
return this.item;
}
// ⚠️ 일반 메서드 T item은 클래스의 <T>를 따라감
public void printItem(T item) {
System.out.println(item);
}
// ✅ 제네릭 메서드 <S>는 <T>와 별개로 독립적
public <S> void printBoxItem(S item) {
System.out.println(item);
}
}
public class Main {
public static void main(String[] args) {
GenericBox<String> strGBox = new GenericBox<>("ABC");
GenericBox<Integer> intGBox = new GenericBox<>(100);
// ⚠️ 일반메서드: 클래스 타입 매개변수를 따라감
// String 데이터 타입 기반으로 타입 소거 발생
// String 타입의 다운캐스팅 코드 삽입!
strGBox.printItem("ABC"); // ✅ String만 사용 가능
strGBox.printItem(100); // ❌ 에러 발생
// ✅ 제네릭 메서드: 독립적인 타입 매개변수를 가짐
// String 타입 정보가 제네릭 메서드에 아무런 영향을 주지 못함
// 다운캐스팅 코드 삽입되지 않음
strGBox.printBoxItem("ABC"); //✅ 모든 데이터 타입 활용 가능
strGBox.printBoxItem(100); //✅ 모든 데이터 타입 활용 가능
strGBox.printBoxItem(0.1); //✅ 모든 데이터 타입 활용 가능
}
}
✅ 다른 관례적인 이름
| 이름 | 의미 |
|---|---|
T | Type(임의의 타입) |
E | Element(컬렉션 요소) |
K | Key(Map의 키) |
V | Value(Map의 값) |
N | Number(숫자) |