JAVA

객체지향(제네릭(Generic))

jki09871 2024. 7. 24. 16:00

제네릭의 주요 개념

  1. 타입 매개변수: 제네릭은 타입을 매개변수처럼 사용할 수 있게 해줍니다. 클래스나 메서드를 정의할 때 타입을 지정하지 않고, 실제 사용할 때 타입을 지정합니다.
  2. 타입 안전성: 제네릭을 사용하면 컴파일 타임에 타입 오류를 방지할 수 있습니다. 이는 런타임에 발생할 수 있는 타입 오류를 사전에 방지하는 데 도움이 됩니다.
  3. 코드 재사용성: 제네릭을 사용하면 동일한 코드를 다양한 데이터 타입으로 재사용할 수 있습니다. 별도의 타입에 대해 각각의 클래스를 작성할 필요가 없습니다.

1. 제네릭 클래스

제네릭 클래스는 클래스 정의 시점에 타입을 지정하지 않고, 클래스 사용 시점에 타입을 지정할 수 있습니다.

public class Box<T> {
    private T item;

    public void setItem(T item) {
        this.item = item;
    }

    public T getItem() {
        return this.item;
    }
}

위 예제에서 Box 클래스는 타입 매개변수 T를 사용합니다. T는 나중에 Box 객체를 생성할 때 지정됩니다.

public class Main {
    public static void main(String[] args) {
        Box<String> stringBox = new Box<>();
        stringBox.setItem("Hello");
        System.out.println(stringBox.getItem());

        Box<Integer> intBox = new Box<>();
        intBox.setItem(123);
        System.out.println(intBox.getItem());
    }
}

위 코드에서 stringBox는 String 타입을, intBox는 Integer 타입을 저장할 수 있습니다.

2. 제네릭 메서드

제네릭 메서드는 메서드 정의 시점에 타입을 지정하지 않고, 메서드 호출 시점에 타입을 지정할 수 있습니다.

public class Util {
    public static <T> void printArray(T[] array) {
        for (T element : array) {
            System.out.print(element + " ");
        }
        System.out.println();
    }
}

위 예제에서 printArray 메서드는 타입 매개변수 T를 사용합니다.

public class Main {
    public static void main(String[] args) {
        Integer[] intArray = {1, 2, 3, 4, 5};
        String[] stringArray = {"A", "B", "C"};

        Util.printArray(intArray);
        Util.printArray(stringArray);
    }
}

위 코드에서 printArray 메서드는 Integer 배열과 String 배열을 처리할 수 있습니다.

3. 제네릭의 제한

제네릭 타입에는 상한 경계를 지정하여 특정 타입의 하위 클래스들만 허용할 수 있습니다.

public class Box<T extends Number> {
    private T item;

    public void setItem(T item) {
        this.item = item;
    }

    public T getItem() {
        return this.item;
    }
}
------------------------------------
public class Main {
    public static void main(String[] args) {
        Box<Integer> intBox = new Box<>();
        intBox.setItem(123);
        System.out.println(intBox.getItem());

        Box<Double> doubleBox = new Box<>();
        doubleBox.setItem(45.67);
        System.out.println(doubleBox.getItem());

        // Box<String> stringBox = new Box<>(); // 오류 발생
        // Error : type argument java.lang.String is not within bounds of type-variable T
        // 해석 : 유형 인수 java.lang.String이 유형 변수 T의 범위 내에 있지 않습니다.
    }
}

위 예제에서 Box 클래스는 Number 타입과 그 하위 타입만을 허용합니다. 따라서 Box<Integer>와 Box<Double>는 가능하지만, Box<String>은 불가능합니다.

 

4. 제네릭을 사용한 간단한 예제

제네릭 클래스 사용 예제

 
// 제네릭 클래스 정의
public class Box<T> {
    private T item;

    // 아이템 설정 메서드
    public void setItem(T item) {
        this.item = item;
    }

    // 아이템 반환 메서드
    public T getItem() {
        return this.item;
    }
}
------------------------------------------------------------

public class Main {
    public static void main(String[] args) {
        GenericList<String> stringList = new GenericList<>();
        stringList.add("Hello");
        stringList.add("World");
        System.out.println("첫 번째 요소: " + stringList.get(0));
        System.out.println("리스트 크기: " + stringList.size());

        GenericList<Integer> intList = new GenericList<>();
        intList.add(10);
        intList.add(20);
        System.out.println("첫 번째 요소: " + intList.get(0));
        System.out.println("리스트 크기: " + intList.size());
    }
}

위 예제에서 GenericList 클래스는 제네릭 타입 T를 사용하여 다양한 타입의 리스트를 만들 수 있습니다. 

 

정리

  • 제네릭 클래스: 타입 매개변수를 사용하여 다양한 데이터 타입을 처리할 수 있는 클래스를 정의합니다. 실제 사용할 때 원하는 타입을 지정합니다.
  • 제네릭 메서드: 메서드에서도 제네릭을 사용하여 다양한 타입의 데이터를 처리할 수 있습니다.

이와 같은 방식으로 제네릭을 사용하면 코드의 재사용성을 높이고, 타입 오류를 컴파일 타임에 방지할 수 있습니다.