[Java 개념] 제네릭

예세림·2024년 7월 23일

Java

목록 보기
8/12
post-thumbnail

제네릭(Generic)이란?

제네릭(Generic)은 클래스나 메서드에서 사용할 데이터 타입을 미리 지정하는 대신, 나중에 필요한 타입으로 지정할 수 있도록 하는 기법입니다. 이는 코드의 재사용성을 높이고, 타입 안전성을 보장합니다.

코드 예시

class Box<T> {
    private T t;
    public T getT() {
        return t;
    }
    public void setT(T t) {
        this.t = t;
    }
}

public class BoxTest {
    public static void main(String[] args) {
        
        Box<Integer> intBox = new Box<Integer>();
        Box<String> strBox = new Box<>();
        Box<Double> doubleBox = new Box<>();
        
        intBox.setT(43);
        strBox.setT("Bonjour");
        String value = strBox.getT(); // 형변환이 필요 없습니다.
    }
}

제네릭 클래스 설명

  • class Box<T>: T는 타입 파라미터로, Box 클래스가 어떤 타입을 담을지 지정합니다.
  • private T t;: Box 클래스는 타입 T의 변수를 가집니다.
  • public T getT(): 타입 T의 값을 반환하는 메서드입니다.
  • public void setT(T t): 타입 T의 값을 설정하는 메서드입니다.

메인 메서드 설명

  • Box<Integer> intBox = new Box<Integer>();: Integer 타입을 담는 Box 객체를 생성합니다.
  • Box<String> strBox = new Box<>();: String 타입을 담는 Box 객체를 생성합니다.
  • Box<Double> doubleBox = new Box<>();: Double 타입을 담는 Box 객체를 생성합니다.
  • intBox.setT(43);: intBox에 43을 설정합니다. 이때 43은 Integer로 오토박싱(autoboxing)됩니다.
  • strBox.setT("Bonjour");: strBox에 "Bonjour" 문자열을 설정합니다.
  • String value = strBox.getT();: strBox에서 값을 가져오며 형 변환이 필요 없습니다.

Wrapper 클래스와 기본형

Wrapper 클래스

  • Java는 기본형 데이터 타입(int, double, etc.)을 객체로 다룰 수 있도록 각 기본형에 대응하는 Wrapper 클래스를 제공합니다. 예: int -> Integer, double -> Double.
  • Wrapper 클래스는 기본형 데이터를 객체처럼 사용할 수 있게 해줍니다.
  • String은 기본형이 아님에 유의합니다.

오토박싱(Auto-Boxing)과 언박싱(Unboxing)

  • 오토박싱(Auto-Boxing): 기본형 데이터를 자동으로 해당하는 Wrapper 클래스 객체로 변환합니다.
    int i1 = 11; // 기본형 변수
    Integer i2 = 11; // 오토박싱: int를 Integer 객체로 변환
  • 언박싱(Unboxing): Wrapper 클래스 객체를 기본형 데이터로 변환합니다.
    Integer i2 = 11; // 오토박싱
    int i3 = i2; // 언박싱: Integer 객체를 int로 변환

결론

제네릭은 코드의 재사용성과 타입 안전성을 높이며, Wrapper 클래스와 오토박싱/언박싱 개념은 기본형 데이터와 객체를 유연하게 사용할 수 있게 합니다. 제네릭을 사용하면 다양한 타입을 안전하게 다룰 수 있으며, 형 변환의 번거로움도 줄일 수 있습니다.

확장

추가된 부분을 포함한 BoxTest 클래스의 전체 코드와 그에 대한 설명을 제공하겠습니다.

class Box<T> {
    private T t;
    public T getT() {
        return t;
    }
    public void setT(T t) {
        this.t = t;
    }
}

public class BoxTest {
    public static void main(String[] args) {
        
        Box<Integer> intBox = new Box<Integer>();
        Box<String> strBox = new Box<>();
        Box<Double> doubleBox = new Box<>();
        
        intBox.setT(43);
        strBox.setT("Bonjour");
        String value = strBox.getT(); // 형변환 노필요. instanceOf.
        
        Box rawBox = new Box(); // Raw 타입 사용
        Box<Object> objBox = new Box<>(); // Object 타입 사용
        
        rawBox.setT(42); // rawBox에 Integer 값 설정 가능
        rawBox.setT("Hello"); // rawBox에 String 값 설정 가능
        Object obj = rawBox.getT(); // 형변환 필요 없음
        
        objBox.setT(42); // objBox에 Integer 값 설정 가능
        objBox.setT("Hello"); // objBox에 String 값 설정 가능
        Object obj2 = objBox.getT(); // 형변환 필요 없음
    }
}

추가된 부분 설명

Raw 타입 사용

Box rawBox = new Box(); // Raw 타입 사용
  • Raw 타입: 제네릭 타입을 지정하지 않고 사용하는 방식입니다. Raw 타입을 사용하면 제네릭의 타입 안전성 보장이 불가능합니다.
  • rawBox.setT(42);: Integer 값 설정 가능.
  • rawBox.setT("Hello");: String 값 설정 가능.
  • Object obj = rawBox.getT();: 반환된 값을 Object 타입으로 받습니다.

Object 타입 사용

Box<Object> objBox = new Box<>(); // Object 타입 사용
  • Box: Object 타입을 가지는 Box 객체입니다. 이는 Raw 타입과 달리 타입 안전성을 보장합니다.

  • objBox.setT(42);: Integer 값 설정 가능.

  • objBox.setT("Hello");: String 값 설정 가능.

  • Object obj2 = objBox.getT();: 반환된 값을 Object 타입으로 받습니다.

    -> Recommended!

  • 결론

    Raw 타입을 사용하면 타입 안전성을 잃게 되므로, 가능한 한 사용을 피하는 것이 좋습니다. 제네릭 타입을 명시적으로 지정하는 것이 타입 안전성을 보장하고 코드의 명확성을 높입니다. Box<Object>를 사용하면 모든 타입의 객체를 담을 수 있지만, 여전히 타입 안전성을 유지할 수 있습니다.

0개의 댓글