<T> (타입 매개변수) 를 의미
코드 재사용성 과 타입 안정성 을 보장받을 수 있음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");
}
}
이 Box 클래스는 속성이 Integer 로 고정되어 있어 재사용을 할 수 없음
다시 사용하려면 다른 클래스를 만들어야 함 (낮은 유연성)
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); // 정수 저장
// ❌ 오류: Integer -> String
String error = (String) objBox.getItem();
System.out.println("잘못된 변환: " + error);
}
}
Object 클래스를 활용하여 다형성을 이용하면 다양한 데이터 타입 저장이 가능함
데이터 조작 시 형 변환이 반드시 필요함
<T> 가 선언된 클래스<T> (타입 매개변수) 를 사용해 다양한 데이터 타입을 안전하게 처리할 수 있음public class GenericBox<T> { // ✅ 제네릭 클래스
private T item;
public GenericBox(T item) {
this.item = item;
}
public T getItem() {
return this.item;
}
}
public class Main {
public static void main(String[] args) {
// 1. ✅ 재사용 가능(컴파일시 타입소거: T -> Object)
GenericBox<String> strGBox = new GenericBox<>("ABC");
GenericBox<Integer> intGBox = new GenericBox<>(100);
GenericBox<Double> doubleGBox = new GenericBox<>(0.1);
// 2. ✅ 타입 안정성 보장(컴파일시 타입소거: 자동으로 다운캐스팅)
String strGBoxItem = strGBox.getItem();
Integer intGBoxItem = intGBox.getItem();
Double doubleGBoxItem = doubleGBox.getItem();
System.out.println("strGBoxItem = " + strGBoxItem);
System.out.println("intGBoxItem = " + intGBoxItem);
System.out.println("doubleGBoxItem = " + doubleGBoxItem);
}
}
📌 타입 소거(Erasure)란?
- 컴파일 시점에 제네릭 타입 정보를 제거하는 과정
<T>부분은 자동으로Object로 대체됨- 필요한 경우 컴파일러가 자동으로 다운 캐스팅 진행
<T> 를 가짐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>(타입 파라미터) 기호 네이밍반복문에서 i 다음으로 j , k 를 사용했던 것처럼
제네릭의 식별자 기호는 통상적으로 아래의 표와 같이 사용한다.
하지만 문법적으로 정해진 건 없음!
| 타입 | 설명 |
|---|---|
<T> | 타입 (Type) |
<E> | 요소, 예를 들어 List |
<K> | 키, 예를 들어 Map<K, V> |
<V> | 리턴 값 또는 매핑된 값 (Variable) |
<N> | 숫자 (Number) |
<S, U, V> | 2, 3, 4번째에 선언된 타입 |
제네릭 타입 자체로 타입을 지정해서 객체를 생성할 수 없다.
즉, new 연산자 뒤에 제네릭 타입 파라미터가 올 수 없다.
class Sample<T> {
public void someMethod() {
// 타입 파라미터로 객체 생성 불가
T t = new T();
}
}
아래처럼 static 변수의 데이터 타입으로 제네릭 타입 파라미터가 올 수 없다.
왜냐하면 static 멤버는 클래스가 동일하게 공유하는 변수로서
제네릭 객체가 생성되기도 전에 이미 자료 타입이 정해져 있어야 하기 때문이다.
즉, 논리적 오류인 것이다.
class Student<T> {
private String name;
private int age = 0;
// static 메서드의 반환 타입으로 사용 불가
public static T addAge(int n) { }
// static 메서드의 매개변수 타입으로 사용 불가
public static void addAge(T n) { }
}
기본적으로 제네릭 클래스 자체를 배열로 만들 수 없다.
class Sample<T> { }
public class Main {
public static void main(String[] args) {
Sample<Integer>[] arr1 = new Sample<>[10];
}
}

하지만 제네릭 타입의 배열 선언은 허용된다.
위의 식과 차이점은 배열에 저장할 Sample 객체의 타입 파라미터를 Integer 로 지정한다는 뜻이다.
즉, new Sample<Integer> 인스턴스는 저장이 가능하며,
new Sample<String>() 인스턴스는 저장이 불가능하다.
class Sample<T> { }
public class Main {
public static void main(String[] args) {
// 에러 발생
Sample<Integer>[] arr1 = new Sample<>[10];
// new Sample<Integer>() 인스턴스만 저장하는 배열을 나타냄
Sample<Integer>[] arr2 = new Sample[10];
arr2[0] = new Sample<>();
arr2[1] = new Sample<>();
// ! Integer가 아닌 타입은 저장 불가능
arr2[2] = new Sample<String>();
}
}
챕터 3-4 : 제네릭(Generic)
자바 제네릭(Generics) 개념 & 문법 정복하기