[Java] 제네릭(Generic)

hyunoi·2024년 11월 14일

Java

목록 보기
7/20
post-thumbnail

제네릭(Generic)


제네릭은 클래스 내부에서 사용할 데이터 타입을 외부에서 지정하는 기법이다.
객체별로 다른 타입의 자료로 저장될 수 있도록 한다.

자바에서 자주 보이는 <> 꺽쇠가 제네릭이다.

ArrayList<Integer> intList = new ArrayList<>();
ArrayList<String> stringList = new ArrayList<>();

위처럼 꺽쇠 안에 데이터 타입을 지정해주면 해당 ArrayList는 Integer, String 데이터만 넣을 수 있게 된다.

제네릭의 장점

  1. 잘못된 타입이 들어가는 것을 컴파일 단계에서 방지할 수 있다 -> 에러 확인 가능

  2. 클래스 외부에서 타입을 지정하기 때문에 따로 타입을 체크하거나 변환할 필요가 없다 -> 관리 편함

  3. 비슷한 기능을 사용하는 경우 코드의 재사용성이 높아진다
    이 경우에 대해 예시를 들어보자면 똑같은 구조의 데이터 타입만 다른 메소드의 경우 제네릭으로 만들면 굉장히 편하고 좋다.

        public static <T> void printList(List<T> list) {
        for (T item : list) {
            System.out.println(item);
        }
    }
    
    public static void main(String[] args) {
        List<String> stringList = Arrays.asList("Alice", "Bob", "Charlie");
        List<Integer> intList = Arrays.asList(1, 2, 3);
    
        printList(stringList); // 문자열 리스트 출력
        printList(intList);    // 정수 리스트 출력
    }

제네릭 사용 방법

public class Box<T> {
    private T item;
    public void setItem(T item) { this.item = item; }
    public T getItem() { return item; }
}

<T>와 같이 타입 매개변수를 설정하여 제네릭을 아래와 같이 사용할 수 있다.

Box<String> stringBox = new Box<>();
Box<Integer> integerBox = new Box<>();
Box<Double> doubleBox = new Box<>();

Box 클래스의 <T>가 들어가는 부분에 각각 String, Integer, Double이 들어가게 되는 것이다.
이를 구체화라고 한다.

타입 매개변수에 할당 가능한 타입

제네릭에는 레퍼런스 타입만 할당이 가능하다. int, double 이런 원시 타입은 할당이 안된다는 것이다.
굳이 int, double이 있는데 Wrapper 클래스의 Integer, Double을 사용하는 것에는 이유가 있는 것

List<int> intList = new List<>();			// 사용 불가능

List<Integer> integerList = new List<>();	// 사용 가능. 내부에서 자동으로 언박싱 해서 원시 타입으로 사용됨

제네릭 타입 제한

제네릭도 모든 타입을 제한 없이 집어넣을 수 있는 것은 아니다.

public <T extends Number> void print(T number) {
    System.out.println(number);
}

이렇게 extends 키워드를 사용하면 Numbers 클래스를 상속받는 타입만 타입 매개변수로 사용이 가능하다.
즉, <T>에 Integer는 들어가지만, String은 들어갈 수 없게 되는 것

와일드카드

제네릭에서 ?를 사용한 와일드카드는 다양한 타입을 수용하면서 유연하게 활용할 수 있게 합니다.
타입이 명확하지는 않지만 특정 타입으로 범위를 지정할 때 사용이 가능하다

<? extends T> T 타입을 상속하는 하위 타입을 허용 (읽기 전용으로 자주 사용)

public static void printNumbers(List<? extends Number> list) {
    for (Number num : list) {
        System.out.println(num);
    }
}

public static void main(String[] args) {
    List<Integer> intList = Arrays.asList(1, 2, 3);
    List<Double> doubleList = Arrays.asList(1.1, 2.2, 3.3);

    printNumbers(intList);     // Integer는 Number의 하위 타입이므로 허용
    printNumbers(doubleList);  // Double도 Number의 하위 타입이므로 허용
}

타입 제한과 와일드 카드의 차이?

여기까지 보았을 때 두 가지 기능의 차이점에 대해 확실하게 모르겠다.

  • 타입 제한은 제네릭 타입을 정의할 때 해당 타입에 대한 명확한 제한을 설정하는 것
  • 와일드 카드는 제네릭 메서드나 클래스 사용 시 타입을 좀 더 유연하게 설정할 수 있도록 하는 것

따라서 타입 제한은 클래스나 메서드 정의에서 특정 타입을 강하게 제한할 때, 와일드카드는 메서드 호출 시 유연하게 사용하고자 할 때 적합하다.
(라고 gpt가 말했다...)


정리
제네릭은 코드의 안정성을 높이고, 유지보수를 쉽게 만들어준다. 다양한 타입에 대해 하나의 코드로 만들어주기 때문에 자바에서 필수적인 기능!

0개의 댓글