제네릭 (Generic) 이란 ?
제네릭은 이름에서도 알 수 있듯, “일반적인(Generic)” 뜻을 지니고 있다. 즉, 데이터 타입을 일반화하여, 여러 타입을 받을 수 있도록 만드는 기능을 담당한다.
💡쉽게 말해:
"데이터 타입을 미리 지정하지 않고, 필요할 때 정할 수 있도록 해주는 기능”
자바에서 배열과 함께 자주 쓰이는 자료형이 리스트인데, 다음과 같이 클래스 선언 문법에 꺽쇠 괄호 <> 로 되어 있는 형태다.
ArrayList<String> list = new ArrayList<>();
저 꺽쇠 괄호를 바로 제네릭이라고 한다. 괄호 안에는 타입 명을 기재할 수 있다.
제네릭이 없는 경우
import java.util.ArrayList;
public class WithoutGenerics {
public static void main(String[] args) {
ArrayList list = new ArrayList(); // 타입을 지정하지 않음
list.add("Hello");
list.add(123); // 숫자도 추가 가능
String str = (String) list.get(0); // 형 변환 필요
System.out.println(str);
}
}
다음의 경우, 두 가지 문제점이 발생한다.
- 타입 안정성이 없다 → 숫자와 문자열을 섞어서 저장할 수 있다.
- 형 변환이 필요하다 → 데이터를 꺼낼 때 (String)처럼 캐스팅해야한다.
제네릭 클래스를 만들면, 클래스에서 데이터타입을 미리 정하지 않고, 객체를 생성할 때 타입을 정할 수 있다.
제네릭 활용 (재사용성 + 타입 안정성)
// 제네릭 클래스 선언
class Box<T> { // T는 타입 매개변수 (Type Parameter)
private T item;
public void setItem(T item) {
this.item = item;
}
public T getItem() {
return item;
}
}
public class GenericExample {
public static void main(String[] args) {
Box<String> stringBox = new Box<>(); // String 타입 지정
stringBox.setItem("Hello");
System.out.println(stringBox.getItem());
Box<Integer> intBox = new Box<>(); // Integer 타입 지정
intBox.setItem(100);
System.out.println(intBox.getItem());
}
}
제네릭을 활용한 결과, 재사용성과 타입 안정성을 보장 받을 수 있다.
- 제네릭 <T>(타입매개변수)
- <T>(타입매개변수) 는 제네릭에서 타입을 의미하는 자리이고, 실제 데이터 타입으로 대체되어 활용된다.
- 타입소거
- 타입 소거는 컴파일 시점에서 제네릭 타입 정보를 제거하는 과정으로 <T> 타입 매개변수 부분을 Object 로 대체하는 것을 의미한다.
- 불필요한 경우, 컴파일러가 자동으로 강제 다운 캐스팅 코드를 삽입하여 타입 안정성을 보장한다.
제네릭 메서드(Generic Method)
public class GenericMethodExample {
// 제네릭 메서드 선언
public static <T> void printItem(T item) {
System.out.println("Item: " + item);
}
public static void main(String[] args) {
printItem("Hello");
printItem(100);
printItem(3.14);
}
}
제네릭은 메서드에서도 사용 가능하며, 리턴 타입이나 매개변수 타입을 유연하게 설정할 수 있다.
제네릭 사용 시 주의사항
1️⃣ static 맴버에는 타입 변수 T 를 사용할 수 없다.
🔎 여기서 잠깐 ! static이란 ?
클래스 로드 시 메모리에 올라가며, 모든 객체가 공유하는 멤버를 정의할 때 사용한다. 객체마다 달라지면 안되는 공용 데이터를 저장할 때 유용하게 쓰이며, 초기 설정 작업을 할 때 적합하다.
제네릭은 인스턴스가 생성될 때 타입이 결정되므로 제네릭 타입은 인스턴스마다 다르다. 하지만 static 맴버는 클래스 레벨에서 공유되므로 객체마다 다른 제네릭 타입을 가질 수 없다. 즉, Box<String>과 Box<Integer>가 각각 다른 타입을 가져야 하는데, static 멤버는 모든 객체에서 공유되므로 모순이 발생하는 것이다.
Class Box<T> {
private T item; // T는 인스턴스가 생성될 때 결정
static T value ; // 에러
}
2️⃣ 제네릭 타입의 배열 T[] 를 생성하는 것은 허용되지 않는다.
제네릭 타입 자체로 타입을 지정하여 객체를 생성하는 것은 불가능하다. new 연산자 뒤에 제네릭 파라미터가 올 수 없다.
Class Box<T> {
T[] itemArr // Ok. T 타입의 배열을 위한 참조변수
T[] toArray(){
T[] tmpArr = new T[itemArr.length]; // 에러. 제네릭 타입의 배열 생성 불가
}
}
'Language > Java' 카테고리의 다른 글
[Java] 스트림 (0) | 2025.03.17 |
---|---|
[Java] if-else와 try-catch에 대해서 (0) | 2025.03.14 |
[Java] 기본형과 참조형 (0) | 2025.03.06 |
[Java] 익명 클래스 (Annoymous Class)와 람다 (Lamda) (0) | 2025.03.05 |
[Java] 컬렉션 (Collection) (0) | 2025.03.04 |