1. 제네릭이란?
- 제네릭(Generic)은 클래스 내부에서 사용할 타입을 외부에서 지정할 수 있게 하는 문법.
- 코드 재사용성과 타입 안정성을 동시에 제공하는 자바의 핵심 기능.
2. 제네릭의 필요성
(1) 문제 상황 1: 타입별 클래스를 반복 구현해야 함
public class IntegerBox { private Integer value; }
public class StringBox { private String value; }
- IntegerBox, StringBox처럼 타입마다 클래스 생성 → 유지보수 어렵고 비효율적
(2) 문제 상황 2: Object 사용시 다운 캐스팅 필요
package com.example.generic.ex1;
public class ObjectBox {
private Object value;
public void set(Object value) {
this.value = value;
}
public Object get() {
return value;
}
}
package com.example.generic.ex1;
public class BoxMain2 {
public static void main(String[] args) {
ObjectBox integerBox = new ObjectBox();
integerBox.set(1);
Integer integer = (Integer) integerBox.get(); // Object -> Integer 다운 캐스팅
System.out.println("integer = " + integer);
ObjectBox stringBox = new ObjectBox();
stringBox.set("hello");
String string = (String) stringBox.get();// Object -> String 다운 캐스팅
System.out.println("string = " + string);
// 잘못된 타입의 인수 전달시
integerBox.set("문자100");
Integer result = (Integer) integerBox.get();
// ClassCastException 발생
System.out.println("result = " + result);
}
}
- 모든 타입을 Object로 받고 꺼낼 때 강제 형변환(casting) 필요 → 타입 안정성 떨어짐
(3) 제네릭(Generic)
public class GenericBox<T> {
private T value;
public void set(T value) { this.value = value; }
public T get() { return value; }
}
GenericBox<Integer> intBox = new GenericBox<>();
intBox.set(10);
Integer result = intBox.get(); // ✅ 안전, 캐스팅 불필요
- T는 타입 매개변수, 사용할 때 실제 타입을 전달하여 사용.
| 용어 | 설명 |
| Generic Type | 제네릭을 사용하는 클래스/인터페이스 |
| 타입 매개변수 | GenericBox<T>의 T |
| 타입 인자 | GenericBox<String>의 String |
| Raw Type | GenericBox처럼 타입 미지정 (지양해야 함) |
제네릭의 장점
| 비교 | 코드 재사용성 | 타입 안정성 |
| 일반 클래스 | ❌ | ✅ |
| Object 사용 | ✅ | ❌ |
| ✅ 제네릭 사용 | ✅ | ✅ |
- 메서드: void print(String value) → 값을 나중에 결정
- 제네릭: class Box<T> → 타입을 나중에 결정
제네릭의 명명 관례
| 이름 | 의미 |
| T | Type |
| E | Element |
| K | Key |
| V | Value |
| S, U, V | 추가 타입 매개변수 |
예제 1) 다음 코드 참고 -> Container클래스 생성
package com.example.generic.test.ex1;
public class ContainerTest {
public static void main(String[] args) {
Container<String> stringContainer = new Container<>();
System.out.println("빈값 확인: " + stringContainer.isEmpty());
stringContainer.setItem("data1");
System.out.println("저장데이터: " + stringContainer.getItem());
System.out.println("빈값 확인2: " + stringContainer.isEmpty());
Container<Integer> integerContainer = new Container<>();
integerContainer.setItem(10);
System.out.println("저장데이터: " + integerContainer.getItem());
}
}
package com.example.generic.test.ex1;
public class Container<T> {
private T item;
public void setItem(T item) {
this.item = item;
}
public T getItem() {
return item;
}
public boolean isEmpty() {
return item == null;
}
}
예제 2) 다음 코드 참고 -> Pair 클래스 생성
package com.example.generic.test.ex2;
public class PairTest {
public static void main(String[] args) {
Pair<Integer, String> pair1 = new Pair<>();
pair1.setFirst(1);
pair1.setData("data");
System.out.println(pair1.getFirst());
System.out.println(pair1.getData());
System.out.println("pair1 = " + pair1);
Pair<String, String> pair2 = new Pair<>();
pair2.setFirst("key");
pair2.setData("value");
System.out.println(pair2.getFirst());
System.out.println(pair2.getData());
System.out.println("pair2 = " + pair2);
}
}
package com.example.generic.test.ex2;
public class Pair<K, V> {
private K key;
private V value;
public void setFirst(K key) {
this.key = key;
}
public void setData(V value) {
this.value = value;
}
public K getFirst() {
return key;
}
public V getData() {
return value;
}
@Override
public String toString() {
return "Pair{" +
"key=" + key +
", value=" + value +
'}';
}
}
✅ 정리
- 제네릭은 타입 안정성을 유지하면서 코드 재사용성을 극대화할 수 있는 강력한 기능이다.
- 실무에서도 List<String>, Map<K, V> 등으로 자주 사용되므로 반드시 숙지.
- 타입 안정성 + 중복 제거 = 제네릭의 진가
'Java' 카테고리의 다른 글
| 자바 중급 2편 - 제네릭(3) (0) | 2025.05.31 |
|---|---|
| 자바 중급 2편 - 제네릭(2) (0) | 2025.05.15 |
| Java 중급 1 - 예외처리(4) (0) | 2025.05.10 |
| Java 중급 1 - 예외처리(3) (0) | 2025.05.05 |
| Java 중급 1 - 예외처리(2) (0) | 2025.05.04 |