-
JAVA - GENERIC 사용방법, 사용이유 파헤치기Java/JAVA17 2025. 6. 10. 15:12반응형
제네릭이란 무엇인가?
제네릭의 기본 문법
// 클래스를 정의할 때 타입 매개변수 T 선언 public class GenericBox<T> { private T value; public void set(T value) { this.value = value; } public T get() { return value; } }
- T는 Type Parameter(타입 매개변수)의 관례적인 이름입니다.
- 실제 사용할 때는 GenericBox<Integer>처럼 타입 인자를 지정해 주면, 그 순간부터 T가 Integer로 고정돼요.
GenericBox<String> strBox = new GenericBox<>(); strBox.set("Hi"); // OK String s = strBox.get(); // 캐스팅 불필요
이제 기본 사용법과 정의를 알아보았으니 진짜 코드를 통하여 왜 필요한지 살펴보자.
1. 왜 제네릭이 필요한가?
1-1. 타입별 전용 Box 클래스
아래의 코드에서는 Integer, String전용 Box클래스를 생성하였다.
// IntegerBox.java public class IntegerBox { private Integer value; public void set(Integer value) { this.value = value; } public Integer get() { return value; } } // StringBox.java public class StringBox { private String value; public void set(String value) { this.value = value; } public String get() { return value; } }
- 장점: 타입 안전성을 보장한다.
- 단점: 박스마다 클래스를 한 개씩 더 만들어야 한다.
위의 경우 Integer, String으로 분류되어있어 객체를 생성하여 사용하는 경우 타입의 안정성을 보장하지만, 타입이 n개가 생긴다면 n개의 타입에 맞는 클래스를 생성해야한다는 단점이 발생한다.
1-2. Object 기반 Box 클래스
// ObjectBox.java public class ObjectBox { private Object value; public void set(Object value) { this.value = value; } public Object get() { return value; } }
public class BoxMain2 { public static void main(String[] args) { ObjectBox box = new ObjectBox(); box.set(10); Integer i = (Integer) box.get(); // ↓ 캐스팅 필요 System.out.println(i); box.set("hello"); String s = (String) box.get(); // ↓ 캐스팅 필요 System.out.println(s); } }
- 장점
- 타입 안전성 보장 (GenericBox<String>엔 String만)
- 코드 재사용성 증가
- 캐스팅 불필요 → 가독성·안정성 향상
Integer, String 등의 제한된 타입이 아닌 Object타입으로 생성한다면 첫번째 방법의 단점 해결이 가능하다.
n개의 클래스를 만들지 않아도 되어 코드 중복을 줄이고 하나의 코드로 재사용성이 증가한다.
3. 조금 더 발전된 예제
3-1. 와일드카드 사용
public static void printBox(GenericBox<? extends Number> box) { Number n = box.get(); System.out.println("숫자: " + n); } public static void main(String[] args) { GenericBox<Integer> ib = new GenericBox<>(); ib.set(123); printBox(ib); GenericBox<Double> db = new GenericBox<>(); db.set(45.6); printBox(db); }
- ? extends Number 의미
- GenericBox<T>에 들어올 수 있는 T 타입을 “Number 또는 그 하위 클래스”로 제한합니다.
- 컴파일러 입장에서는 box 내부에 어떤 정확한 타입이 들어있는지는 알 수 없고, 단지 “Number의 어떤 서브타입”이라는 것만 보장하죠.
- 왜 get()만 가능하고 set()은 불가능할까?
- ? extends Number로 선언된 곳에 box.set(...)을 하면, 컴파일러는 “지금 박스가 GenericBox<Integer>인지 GenericBox<Double>인지 모른다”라고 판단해 에러를 냅니다.
- 반면 get()은 언제나 최소한 Number 타입을 반환한다는 사실이 보장되므로, Number n = box.get();은 안전합니다.
- PECS 원칙
- Producer Extends, Consumer Super
- 읽기(프로듀스) 전용일 때 <? extends T>를, 쓰기(컨슘) 전용일 때 <? super T>를 사용하라는 가이드입니다.
- 여기서는 “숫자를 꺼내서 출력만 하겠다”는 의도이므로 extends를 씁니다.
3-2. 타입 제한(바운디드) 제네릭 메서드
public class Util { // 숫자 타입만 더할 수 있는 메서드 public static <T extends Number> double add(T a, T b) { return a.doubleValue() + b.doubleValue(); } } public class BoxMain4 { public static void main(String[] args) { double sum1 = Util.add(3, 7); // OK: Integer → Number double sum2 = Util.add(2.5, 4.5); // OK: Double → Number System.out.println(sum1 + ", " + sum2); } }
- 메서드 선언부 해체
- <T extends Number>
- 이 메서드만의 타입 매개변수 T를 도입하고, T는 Number 혹은 하위 클래스여야 함을 지정합니다.
- double add(T a, T b)
- 두 개의 T 인자를 받아 double 결과를 반환합니다.
- <T extends Number>
- 왜 double을 반환하나?
- Number 추상 클래스는 intValue(), longValue(), doubleValue() 등을 제공하는데, 일반적으로 소수점까지 안전하게 다루기 위해 doubleValue()로 변환 후 합산합니다.
반응형'Java > JAVA17' 카테고리의 다른 글
[JAVA] - 1.8 -> 17 버전 업그레이드 Base64오류 (0) 2025.06.27 JAVA - Generic을 사용해야하는 이유 (1) 2025.06.12 JAVA - 다형성, 추상메서드, 인터페이스 사용하는 이유 (0) 2024.11.12 JAVA - 다형성 Override (0) 2024.11.12 JAVA - 다형성 (업케스팅, 다운케스팅, instanceof) (0) 2024.11.12