ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • 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);
        }
    }
    • 장점
      1. 타입 안전성 보장 (GenericBox<String>엔 String만)
      2. 코드 재사용성 증가
      3. 캐스팅 불필요 → 가독성·안정성 향상

    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 결과를 반환합니다.
    • 왜 double을 반환하나?
      • Number 추상 클래스는 intValue(), longValue(), doubleValue() 등을 제공하는데, 일반적으로 소수점까지 안전하게 다루기 위해 doubleValue()로 변환 후 합산합니다.

     


     

    반응형

    댓글

Designed by Tistory.