프로그래밍 공부
[자바] #07. 제네릭 (Generics) 본문
📢 제네릭이란 ?
데이터 타입을 매개변수화 할 수 있는 기능.
클래스 내부에서 사용할 데이터 타입을 미리 지정하지 않고, 인스턴스를 생성할 때 타입을 정할 수 있다.
형 변환(다운캐스팅)이 필요 없고, 컴파일 시점에 타입을 검사하여 안정성이 높아진다.
👀 제네릭 클래스 예제
class Apple {
public String toString() { return "I am apple."; }
}
class Grape {
public String toString() { return "I am grape."; }
}
class Box<T> { // 제네릭으로 클래스 만들기
private T o;
public void set(T o) { this.o = o; }
}
public class Main {
public static void main(String[] args) {
Box<Apple> appleBox = new Box<>(); // Apple이나 Apple을 상속하는 클래스의 인스턴스 저장 가능
Box<Grape> grapeBox = new Box<>(); // Grape나 Grape를 상속하는 클래스의 인스턴스 저장 가능
}
}
🔔 용어 정리
- 타입 매개변수 (Type Parameter) : Box<T>에서의 T
- 타입 인자 (Type Argument) : Box<Apple>에서의 Apple
- 매개변수화 타입 (Parameterized Type) : Box<Apple> 이라는 새로운 자료형
👀 제네릭 메서드 예제
제네릭은 클래스뿐만 아니라 메서드에도 적용 가능하다.
class Util {
public static <T> void printArray(T[] array) { // 제네릭 메서드
for (T element : array) {
System.out.print(element + " ");
}
System.out.println();
}
}
public class Main {
public static void main(String[] args) {
Integer[] intArray = {1, 2, 3, 4, 5};
String[] strArray = {"A", "B", "C"};
Util.printArray(intArray); // 1 2 3 4 5
Util.printArray(strArray); // A B C
}
}
메서드에 <T>를 선언하여, 호출 시점에 타입을 지정할 수 있다.
🚨 제네릭의 제한 사항
1. 기본형(Primitive Type) 사용 불가
Box<int> intBox = new Box<int>(); // ❌
✅ 제네릭은 객체 타입(Reference Type)만 사용 가능하다.
✅ 기본형을 사용하려면 Wrapper 클래스(Integer, Double 등)를 사용해야.
2. 타입 매개변수로 static 변수 사용 불가
class Test<T> {
// static T value; // ❌
}
✅ T는 인스턴스 생성 시에 정해지기 때문에, 클래스 레벨의 static 변수에 사용할 수 없다.
3. 타입 매개변수로 객체 생성 불가
class Box<T> {
private T value;
public Box() {
// value = new T(); // ❌
}
}
✅ 제네릭에서 T는 컴파일 시점에 실제 타입이 결정되는 것이 아니라, 런타임 시점에 특정 타입으로 치환된다.
✅ 하지만 자바에서는 new 키워드를 사용할 때, 반드시 컴파일 시점에 타입이 명확해야 한다.
- T가 어떤 타입인지 컴파일 타임에 알 수 없음.
- T가 기본형(int, double)인지, 객체(String, Integer)인지 알 수 없음.
- 생성자 호출 방식(T가, 매개변수가 없는 기본 생성자가 있는지) 또한 모름.
📢 '매개변수화 타입'도 타입 인자로 전달 가능
상자를 하나 생성해서 그 안에 문자열을 저장한 후 이 상자를 다른 상자에 넣고,
이 상자를 한 번 더 다른 상자에 넣을 수도 있다. 즉 문자열을 세 개의 상자로 겹겹이 두르는 것이다.
class Box<T> {
private T o;
public void set(T o) { this.o = o; }
public T get() { return o; }
}
class BoxInBox {
public static void main(String[] args) {
Box<String> sBox = new Box<>();
sBox.set("I am happy");
Box<Box<String>> wBox = new Box<>();
wBox.set(sBox);
Box<Box<Box<String>>> zBox = new Box<>();
zBox.set(wBox));
System.out.println(zBox.get().get().get()); // I am happy 출력
}
}
📢 제네릭 클래스의 타입 인자를 제한할 수도
class Box<T extends Eatable>
이는 인스턴스 생성 시 T 타입 인자를 받을 때
Eatable이 인터페이스라면 T는 반드시 그 인터페이스를 구현해야 하고,
Eatable이 클래스라면 T는 그 클래스를 상속해야 한다.
이와 마찬가지로 제네릭 메서드 또한 호출 시 타입 인자를 제한할 수 있다.
public static <T extends Eatable> Box<T> makeBox(T o)
이는 Box<T>형을 반환하는 메서드이다.
반환형 앞의 꺽쇠 <T extends Eatable>은 T가 타입 매개변수이고, 이 T는 Eatable을 구현 또는 상속해야 한다는 의미이다.
'자바' 카테고리의 다른 글
| [자바] #09. 와일드카드 (Wildcard) - 2. 상한 제한과 하한 제한 (0) | 2025.02.15 |
|---|---|
| [자바] #08. 와일드카드 (Wildcard) - 1. 제네릭 <T>와 와일드카드 (?) (0) | 2025.02.15 |
| [자바] #06. 배열의 정렬 sort (0) | 2025.02.15 |
| [자바] #05. Object 클래스에 정의되어 있는 주요 메소드 (equals, clone) (0) | 2025.02.14 |
| [자바] #04. 자바에서의 메모리 구조 - 메소드 영역과 힙 영역 (0) | 2025.02.14 |